1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 10:52:53 +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:**
**Logs:**
<!--
*please attach logs here, which are located at:*
- `%AppData%/osu/logs` *(on Windows),*
- `~/.local/share/osu/logs` *(on Linux & macOS).*
-->

View File

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

42
.vscode/launch.json vendored
View File

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

View File

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

View File

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

View File

@ -47,20 +47,25 @@ namespace osu.Desktop
return null;
}
protected override UpdateManager CreateUpdateManager()
{
switch (RuntimeInfo.OS)
{
case RuntimeInfo.Platform.Windows:
return new SquirrelUpdateManager();
default:
return new SimpleUpdateManager();
}
}
protected override void LoadComplete()
{
base.LoadComplete();
if (!noVersionOverlay)
{
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);
}

View File

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

View File

@ -3,8 +3,8 @@
using System;
using osu.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Graphics;
using osu.Framework.Layout;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
using osuTK;
@ -22,21 +22,13 @@ namespace osu.Game.Rulesets.Mania.Mods
private class ManiaFlashlight : Flashlight
{
private readonly Cached flashlightProperties = new Cached();
private readonly LayoutValue flashlightProperties = new LayoutValue(Invalidation.DrawSize);
public ManiaFlashlight()
{
FlashlightSize = new Vector2(0, default_flashlight_size);
}
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);
AddLayout(flashlightProperties);
}
protected override void Update()

View File

@ -2,13 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Caching;
using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Layout;
using osu.Game.Graphics;
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()
@ -100,15 +102,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
}
}
private readonly Cached subtractionCache = new Cached();
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);
}
private readonly LayoutValue subtractionCache = new LayoutValue(Invalidation.DrawSize);
protected override void Update()
{

View File

@ -115,9 +115,8 @@ namespace osu.Game.Rulesets.Mania.UI
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
RelativeSizeAxes = Axes.Both,
Y = HIT_TARGET_POSITION + 150,
BypassAutoSizeAxes = 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.
using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Osu.Objects;
@ -114,6 +117,22 @@ namespace osu.Game.Rulesets.Osu.Tests
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[]
{
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 FollowPointConnection getGroup(int index) => followPointRenderer.Connections[index];

View File

@ -3,13 +3,13 @@
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests
{
public class TestSceneOsuFlashlight : TestSceneOsuPlayer
{
protected override Player CreatePlayer(Ruleset ruleset)
protected override TestPlayer CreatePlayer(Ruleset ruleset)
{
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.Sprites;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Screens.Play;
using osu.Game.Skinning;
using osu.Game.Storyboards;
using osu.Game.Tests.Visual;
@ -56,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private void checkNextHitObject(string 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)
return false;
@ -75,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.Tests
[Resolved]
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);

View File

@ -11,7 +11,6 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Tests.Visual;
using osuTK;
using System.Collections.Generic;
using System.Linq;
@ -44,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests
base.SetUpSteps();
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]
@ -89,7 +88,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
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

View File

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

View File

@ -5,7 +5,6 @@ using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using osu.Framework.Allocation;
using osu.Framework.Caching;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Batches;
using osu.Framework.Graphics.OpenGL.Vertices;
@ -14,6 +13,7 @@ using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Layout;
using osu.Framework.Timing;
using osuTK;
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
parts[i].InvalidationID = -1;
}
AddLayout(partSizeCache);
}
[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
? partSizeCache.Value
: (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>
/// The amount of time to fade the cursor trail pieces.
/// </summary>
@ -97,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
{
base.Update();
Invalidate(Invalidation.DrawNode, shallPropagate: false);
Invalidate(Invalidation.DrawNode);
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.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Taiko.Tests
{
public class TestSceneSwellJudgements : PlayerTestScene
{
protected new TestPlayer Player => (TestPlayer)base.Player;
public TestSceneSwellJudgements()
: base(new TaikoRuleset())
{
@ -49,25 +42,5 @@ namespace osu.Game.Rulesets.Taiko.Tests
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 NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Taiko.Tests
@ -22,10 +20,10 @@ namespace osu.Game.Rulesets.Taiko.Tests
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();
return new ScoreAccessiblePlayer();
return base.CreatePlayer(ruleset);
}
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) =>
@ -49,20 +47,10 @@ namespace osu.Game.Rulesets.Taiko.Tests
AddStep("Setup judgements", () =>
{
judged = false;
((ScoreAccessiblePlayer)Player).ScoreProcessor.NewJudgement += b => judged = true;
Player.ScoreProcessor.NewJudgement += b => judged = true;
});
AddUntilStep("swell judged", () => judged);
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.
using osu.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Graphics;
using osu.Framework.Layout;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.UI;
@ -30,13 +30,15 @@ namespace osu.Game.Rulesets.Taiko.Mods
private class TaikoFlashlight : Flashlight
{
private readonly Cached flashlightProperties = new Cached();
private readonly LayoutValue flashlightProperties = new LayoutValue(Invalidation.DrawSize);
private readonly TaikoPlayfield taikoPlayfield;
public TaikoFlashlight(TaikoPlayfield taikoPlayfield)
{
this.taikoPlayfield = taikoPlayfield;
FlashlightSize = new Vector2(0, getSizeFor(0));
AddLayout(flashlightProperties);
}
private float getSizeFor(int combo)
@ -56,16 +58,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
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()
{
base.Update();

View File

@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Background
private DummySongSelect songSelect;
private TestPlayerLoader playerLoader;
private TestPlayer player;
private LoadBlockingTestPlayer player;
private BeatmapManager manager;
private RulesetStore rulesets;
@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.Background
public void PlayerLoaderSettingsHoverTest()
{
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);
AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent());
AddStep("Trigger background preview", () =>
@ -268,7 +268,7 @@ namespace osu.Game.Tests.Visual.Background
{
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);
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);
}
private class TestPlayer : Visual.TestPlayer
private class LoadBlockingTestPlayer : TestPlayer
{
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> IsPaused = new Bindable<bool>();
public TestPlayer(bool allowPause = true)
public LoadBlockingTestPlayer(bool allowPause = true)
: base(allowPause)
{
}

View File

@ -5,7 +5,6 @@ using System.ComponentModel;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Storyboards;
@ -14,25 +13,24 @@ namespace osu.Game.Tests.Visual.Gameplay
[Description("Player instantiated with an autoplay mod.")]
public class TestSceneAutoplay : TestSceneAllRulesetPlayers
{
protected new TestPlayer Player => (TestPlayer)base.Player;
protected override Player CreatePlayer(Ruleset ruleset)
{
SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return new ScoreAccessiblePlayer();
return new TestPlayer(false, false);
}
protected override void AddCheckSteps()
{
ScoreAccessiblePlayer scoreAccessiblePlayer = null;
AddUntilStep("player loaded", () => (scoreAccessiblePlayer = (ScoreAccessiblePlayer)Player) != null);
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("score above zero", () => Player.ScoreProcessor.TotalScore.Value > 0);
AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2));
AddStep("seek to break time", () => Player.GameplayClockContainer.Seek(Player.BreakOverlay.Breaks.First().StartTime));
AddUntilStep("wait for seek to complete", () =>
scoreAccessiblePlayer.HUDOverlay.Progress.ReferenceClock.CurrentTime >= scoreAccessiblePlayer.BreakOverlay.Breaks.First().StartTime);
AddAssert("test keys not counting", () => !scoreAccessiblePlayer.HUDOverlay.KeyCounter.IsCounting);
AddStep("rewind", () => scoreAccessiblePlayer.GameplayClockContainer.Seek(-80000));
AddUntilStep("key counter reset", () => scoreAccessiblePlayer.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= Player.BreakOverlay.Breaks.First().StartTime);
AddAssert("test keys not counting", () => !Player.HUDOverlay.KeyCounter.IsCounting);
AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000));
AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
}
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
@ -41,19 +39,5 @@ namespace osu.Game.Tests.Visual.Gameplay
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.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
@ -11,12 +10,8 @@ using osu.Framework.Utils;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu;
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 osuTK;
@ -24,8 +19,6 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public class TestSceneGameplayRewinding : PlayerTestScene
{
private RulesetExposingPlayer player => (RulesetExposingPlayer)Player;
[Resolved]
private AudioManager audioManager { get; set; }
@ -48,13 +41,13 @@ namespace osu.Game.Tests.Visual.Gameplay
{
AddUntilStep("wait for track to start running", () => track.IsRunning);
addSeekStep(3000);
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));
AddStep("clear results", () => player.AppliedResults.Clear());
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));
AddStep("clear results", () => Player.Results.Clear());
addSeekStep(0);
AddAssert("none judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged));
AddUntilStep("key counters reset", () => player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
AddAssert("no results triggered", () => player.AppliedResults.Count == 0);
AddAssert("none judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged));
AddUntilStep("key counters reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
AddAssert("no results triggered", () => Player.Results.Count == 0);
}
private void addSeekStep(double time)
@ -62,13 +55,13 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep($"seek to {time}", () => track.Seek(time));
// 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();
return new RulesetExposingPlayer();
return base.CreatePlayer(ruleset);
}
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
@ -89,29 +82,5 @@ namespace osu.Game.Tests.Visual.Gameplay
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.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play;
using osuTK;
using osuTK.Input;
@ -282,14 +281,10 @@ namespace osu.Game.Tests.Visual.Gameplay
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
{
public new HealthProcessor HealthProcessor => base.HealthProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
public bool FailOverlayVisible => FailOverlay.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.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual.Gameplay
{
[HeadlessTest] // we alter unsafe properties on the game host to test inactive window state.
public class TestScenePauseWhenInactive : PlayerTestScene
{
protected new TestPlayer Player => (TestPlayer)base.Player;
protected override IBeatmap CreateBeatmap(RulesetInfo 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);
}
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 osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
@ -307,17 +306,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
}
private class TestPlayer : Visual.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
protected class SlowLoadPlayer : TestPlayer
{
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>();
frameworkConfig.GetBindable<double>(FrameworkSetting.CursorSensitivity).Disabled = false;
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);
CreateGame();
});
AddUntilStep("Wait for load", () => Game.IsLoaded);
@ -78,6 +71,18 @@ namespace osu.Game.Tests.Visual.Navigation
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)
{
Screen screen = null;
@ -97,12 +102,17 @@ namespace osu.Game.Tests.Visual.Navigation
public new SettingsPanel Settings => base.Settings;
public new MusicController MusicController => base.MusicController;
public new OsuConfigManager LocalConfig => base.LocalConfig;
public new Bindable<WorkingBeatmap> Beatmap => base.Beatmap;
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();
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);
}
[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() =>
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.
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets;
using osu.Game.Users;
using osuTK;
@ -13,13 +15,19 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
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;
Add(new FillFlowContainer
Child = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -44,34 +52,38 @@ namespace osu.Game.Tests.Visual.Online
SupportLevel = 3,
}) { Width = 300 },
},
});
};
flyte.Status.Value = new UserStatusOnline();
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);
});
AddStep("idle", () => { activity.Value = null; });
AddStep("spectating", () => { activity.Value = new UserActivity.Spectating(); });
AddStep("solo", () => { activity.Value = new UserActivity.SoloGame(null, null); });
AddStep("choosing", () => { activity.Value = new UserActivity.ChoosingBeatmap(); });
AddStep("editing", () => { activity.Value = new UserActivity.Editing(null); });
AddStep("modding", () => { activity.Value = new UserActivity.Modding(); });
[Test]
public void TestUserStatus()
{
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 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.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN");
match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals");
match.Completed.Value = true;
ladder.CurrentMatch.Value = match;
Add(new TeamWinScreen

View File

@ -5,7 +5,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osuTK;
using osuTK.Graphics;
@ -35,7 +34,7 @@ namespace osu.Game.Tournament.Components
RelativeSizeAxes = Axes.Both,
Colour = new Color4(54, 54, 54, 255)
},
new OsuSpriteText
new TournamentSpriteText
{
Anchor = 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.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Models;
namespace osu.Game.Tournament.Components
@ -19,7 +18,7 @@ namespace osu.Game.Tournament.Components
public readonly TournamentTeam Team;
protected readonly Sprite Flag;
protected readonly OsuSpriteText AcronymText;
protected readonly TournamentSpriteText AcronymText;
[UsedImplicitly]
private Bindable<string> acronym;
@ -37,9 +36,9 @@ namespace osu.Game.Tournament.Components
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.Legacy;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets;
using osu.Game.Screens.Menu;
using osuTK;
@ -262,7 +261,7 @@ namespace osu.Game.Tournament.Components
static void cp(SpriteText s, Color4 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++)
@ -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(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.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Models;
using osuTK;
using osuTK.Graphics;
@ -77,14 +76,14 @@ namespace osu.Game.Tournament.Components
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OsuSpriteText
new TournamentSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = new LocalisedString((
$"{Beatmap.Metadata.ArtistUnicode ?? Beatmap.Metadata.Artist} - {Beatmap.Metadata.TitleUnicode ?? 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
{
@ -95,28 +94,28 @@ namespace osu.Game.Tournament.Components
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new OsuSpriteText
new TournamentSpriteText
{
Text = "mapper",
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,
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",
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,
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.
using System;
using System.Linq;
using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Game.Users;
@ -29,6 +30,32 @@ namespace osu.Game.Tournament.Models
/// </summary>
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]
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.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
using osuTK;
@ -43,7 +42,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
Colour = new Color4(54, 54, 54, 255)
},
// Group name
new OsuSpriteText
new TournamentSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
@ -51,7 +50,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
Position = new Vector2(0, 7f),
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),
},
teams = new FillFlowContainer<GroupTeam>
@ -134,7 +133,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
AcronymText.Anchor = Anchor.TopCentre;
AcronymText.Origin = Anchor.TopCentre;
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[]
{

View File

@ -14,7 +14,6 @@ using osu.Framework.Graphics.Textures;
using osu.Framework.Logging;
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.Drawings.Components;
@ -29,7 +28,7 @@ namespace osu.Game.Tournament.Screens.Drawings
private ScrollingTeamContainer teamsContainer;
private GroupContainer groupsContainer;
private OsuSpriteText fullTeamNameText;
private TournamentSpriteText fullTeamNameText;
private readonly List<TournamentTeam> allTeams = new List<TournamentTeam>();
@ -109,18 +108,18 @@ namespace osu.Game.Tournament.Screens.Drawings
RelativeSizeAxes = Axes.X,
},
// Scrolling team name
fullTeamNameText = new OsuSpriteText
fullTeamNameText = new TournamentSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
Position = new Vector2(0, 45f),
Colour = OsuColour.Gray(0.33f),
Colour = OsuColour.Gray(0.95f),
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;
[Resolved(canBeNull: true)]
private TournamentSceneManager sceneManager { get; set; }
[Resolved]
private LadderInfo ladderInfo { get; set; }
@ -113,6 +116,18 @@ namespace osu.Game.Tournament.Screens.Editors
Width = 0.2f,
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
{
Width = 0.11f,
@ -131,7 +146,17 @@ namespace osu.Game.Tournament.Screens.Editors
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.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
@ -30,7 +30,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
new TournamentLogo(),
new RoundDisplay
{
Y = 10,
Y = 5,
Anchor = Anchor.BottomCentre,
Origin = Anchor.TopCentre,
},
@ -51,9 +51,6 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
{
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<TournamentTeam> currentTeam = new Bindable<TournamentTeam>();
private readonly Bindable<int?> currentTeamScore = new Bindable<int?>();
@ -106,7 +103,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
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;
InternalChildren = new Drawable[]
@ -169,13 +166,13 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
Children = new Drawable[]
{
Flag,
new OsuSpriteText
new TournamentSpriteText
{
Text = team?.FullName.Value.ToUpper() ?? "???",
X = (flip ? -1 : 1) * 90,
Y = -10,
Colour = colour,
Font = TournamentFont.GetFont(typeface: TournamentTypeface.Aquatico, weight: FontWeight.Regular, size: 20),
Font = OsuFont.Torus.With(weight: FontWeight.Regular, size: 20),
Origin = anchor,
Anchor = anchor,
},
@ -188,10 +185,31 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
{
private readonly Bindable<TournamentMatch> currentMatch = new Bindable<TournamentMatch>();
private readonly TournamentSpriteText text;
public RoundDisplay()
{
Width = 200;
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]
@ -201,20 +219,8 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
currentMatch.BindTo(ladder.CurrentMatch);
}
private void matchChanged(ValueChangedEvent<TournamentMatch> match)
{
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),
},
};
}
private void matchChanged(ValueChangedEvent<TournamentMatch> match) =>
text.Text = match.NewValue.Round.Value?.Name.Value ?? "Unknown Round";
}
}
}

View File

@ -123,8 +123,8 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
public bool Winning
{
set => DisplayedCountSpriteText.Font = value
? TournamentFont.GetFont(typeface: TournamentTypeface.Aquatico, weight: FontWeight.Regular, size: 60)
: TournamentFont.GetFont(typeface: TournamentTypeface.Aquatico, weight: FontWeight.Light, size: 40);
? OsuFont.Torus.With(weight: FontWeight.Regular, size: 60)
: 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.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
@ -26,7 +25,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
{
private readonly TournamentMatch match;
private readonly bool losers;
private OsuSpriteText scoreText;
private TournamentSpriteText scoreText;
private Box background;
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.Padding = new MarginPadding { Left = 50 };
AcronymText.Font = OsuFont.GetFont(size: 24);
AcronymText.Font = OsuFont.Torus.With(size: 24);
if (match != null)
{
@ -119,11 +118,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
Alpha = 0.8f,
RelativeSizeAxes = Axes.Both,
},
scoreText = new OsuSpriteText
scoreText = new TournamentSpriteText
{
Anchor = 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);
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

View File

@ -6,7 +6,6 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Models;
using osuTK.Graphics;
@ -22,8 +21,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
public DrawableTournamentRound(TournamentRound round, bool losers = false)
{
OsuSpriteText textName;
OsuSpriteText textDescription;
TournamentSpriteText textName;
TournamentSpriteText textDescription;
AutoSizeAxes = Axes.Both;
InternalChild = new FillFlowContainer
@ -32,15 +31,15 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
textDescription = new OsuSpriteText
textDescription = new TournamentSpriteText
{
Colour = Color4.Black,
Origin = 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,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre

View File

@ -9,7 +9,6 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Settings;
using osu.Game.Screens.Play.PlayerSettings;
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,
Children = new Drawable[]
{
new TourneyVideo(storage.GetStream(@"BG Side Logo - OWC.m4v"))
new TourneyVideo(storage.GetStream(@"videos/ladder.m4v"))
{
RelativeSizeAxes = Axes.Both,
Loop = true,
@ -80,7 +80,7 @@ namespace osu.Game.Tournament.Screens.Ladder
break;
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))
d.Expire();

View File

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

View File

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

View File

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

View File

@ -10,7 +10,7 @@ namespace osu.Game.Tournament.Screens.Showcase
[BackgroundDependencyLoader]
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 TournamentLogo(bool includeRoundBackground = true)
public TournamentLogo()
{
RelativeSizeAxes = Axes.X;
Margin = new MarginPadding { Vertical = 5 };
if (includeRoundBackground)
{
AutoSizeAxes = Axes.Y;
}
else
{
Masking = true;
Height = 100;
}
Height = 100;
}
[BackgroundDependencyLoader]
@ -32,9 +24,11 @@ namespace osu.Game.Tournament.Screens.Showcase
{
InternalChild = new Sprite
{
Texture = textures.Get("game-screen-logo"),
Anchor = 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.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.Showcase;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tournament.Screens.TeamIntro
{
@ -29,12 +26,11 @@ namespace osu.Game.Tournament.Screens.TeamIntro
InternalChildren = new Drawable[]
{
new TourneyVideo(storage.GetStream(@"BG Team - Both OWC.m4v"))
new TourneyVideo(storage.GetStream(@"videos/teamintro.m4v"))
{
RelativeSizeAxes = Axes.Both,
Loop = true,
},
new TournamentLogo(false),
mainContainer = new Container
{
RelativeSizeAxes = Axes.Both,
@ -75,8 +71,9 @@ namespace osu.Game.Tournament.Screens.TeamIntro
{
RelativeSizeAxes = Axes.Both,
Height = 0.25f,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Y = 180,
}
};
}
@ -85,8 +82,6 @@ namespace osu.Game.Tournament.Screens.TeamIntro
{
public RoundDisplay(TournamentMatch match)
{
var col = OsuColour.Gray(0.33f);
InternalChildren = new Drawable[]
{
new FillFlowContainer
@ -98,31 +93,13 @@ namespace osu.Game.Tournament.Screens.TeamIntro
Spacing = new Vector2(0, 10),
Children = new Drawable[]
{
new OsuSpriteText
new TournamentSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Colour = col,
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,
Colour = OsuColour.Gray(0.33f),
Text = match.Round.Value?.Name.Value ?? "Unknown Round",
Spacing = new Vector2(10, 0),
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)
Font = OsuFont.Torus.With(size: 26, weight: FontWeight.Light)
},
}
}
@ -132,21 +109,19 @@ namespace osu.Game.Tournament.Screens.TeamIntro
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)
{
FillFlowContainer players;
var colour = left ? red : blue;
var colour = left ? TournamentGame.COLOUR_RED : TournamentGame.COLOUR_BLUE;
InternalChildren = new Drawable[]
{
new TeamDisplay(team, left ? "Team Red" : "Team Blue", colour)
new TeamDisplay(team)
{
Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft,
Origin = Anchor.Centre,
Origin = Anchor.TopCentre,
RelativePositionAxes = Axes.Both,
X = (left ? -1 : 1) * 0.36f,
X = (left ? -1 : 1) * 0.3145f,
Y = -0.077f,
},
players = new FillFlowContainer
{
@ -157,7 +132,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft,
Origin = left ? Anchor.CentreRight : Anchor.CentreLeft,
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)
{
players.Add(new OsuSpriteText
players.Add(new TournamentSpriteText
{
Text = p.Username,
Font = OsuFont.GetFont(size: 24),
Font = OsuFont.Torus.With(size: 24),
Colour = colour,
Anchor = 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
{
public TeamDisplay(TournamentTeam team, string teamName, Color4 colour)
public TeamDisplay(TournamentTeam team)
: base(team)
{
AutoSizeAxes = Axes.Both;
@ -187,33 +162,24 @@ namespace osu.Game.Tournament.Screens.TeamIntro
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 };
Flag.Scale = new Vector2(0.32f);
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Spacing = new Vector2(160),
Children = new Drawable[]
{
Flag,
new OsuSpriteText
new TournamentSpriteText
{
Text = team?.FullName.Value.ToUpper() ?? "???",
Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 40, FontWeight.Light),
Colour = Color4.Black,
Text = team?.FullName.Value ?? "???",
Font = OsuFont.Torus.With(size: 20, weight: FontWeight.Regular),
Colour = OsuColour.Gray(0.2f),
Origin = 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.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.Showcase;
using osuTK;
using osuTK.Graphics;
@ -33,22 +31,18 @@ namespace osu.Game.Tournament.Screens.TeamWin
InternalChildren = new Drawable[]
{
blueWinVideo = new TourneyVideo(storage.GetStream(@"BG Team - Win Blue.m4v"))
blueWinVideo = new TourneyVideo(storage.GetStream(@"videos/teamwin-blue.m4v"))
{
Alpha = 1,
RelativeSizeAxes = Axes.Both,
Loop = true,
},
redWinVideo = new TourneyVideo(storage.GetStream(@"BG Team - Win Red.m4v"))
redWinVideo = new TourneyVideo(storage.GetStream(@"videos/teamwin-red.m4v"))
{
Alpha = 0,
RelativeSizeAxes = Axes.Both,
Loop = true,
},
new TournamentLogo(false)
{
Y = 40,
},
mainContainer = new Container
{
RelativeSizeAxes = Axes.Both,
@ -85,141 +79,99 @@ namespace osu.Game.Tournament.Screens.TeamWin
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)
{
RelativeSizeAxes = Axes.Both,
Width = 0.5f,
Height = 0.6f,
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 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)
{
var colour = left ? red : blue;
FillFlowContainer players;
var colour = left ? TournamentGame.COLOUR_RED : TournamentGame.COLOUR_BLUE;
InternalChildren = new Drawable[]
{
new TeamDisplay(team, left ? "Team Red" : "Team Blue", colour)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new FillFlowContainer
{
Direction = FillDirection.Vertical,
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[]
{
Flag,
new OsuSpriteText
new TournamentSpriteText
{
Text = team?.FullName.Value.ToUpper() ?? "???",
Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 40, FontWeight.Light),
Text = "WINNER",
Font = OsuFont.Torus.With(size: 24, weight: FontWeight.SemiBold),
Colour = Color4.Black,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
},
new OsuSpriteText
new TournamentSpriteText
{
Text = teamName.ToUpper(),
Font = OsuFont.GetFont(size: 20),
Colour = colour,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
}
Text = team?.FullName.Value ?? "???",
Font = OsuFont.Torus.With(size: 30, weight: FontWeight.SemiBold),
Colour = Color4.Black,
},
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.Game.Graphics.Cursor;
using osuTK.Graphics;
namespace osu.Game.Tournament
{
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()
{
base.LoadComplete();

View File

@ -18,7 +18,6 @@ using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests;
using osu.Game.Tournament.IPC;
using osu.Game.Tournament.Models;
@ -54,9 +53,6 @@ namespace osu.Game.Tournament
{
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))));
this.storage = storage;
@ -104,10 +100,10 @@ namespace osu.Game.Tournament
Colour = Color4.Red,
RelativeSizeAxes = Axes.Both,
},
new OsuSpriteText
new TournamentSpriteText
{
Text = "Please make the window wider",
Font = OsuFont.Default.With(weight: "bold"),
Font = OsuFont.Torus.With(weight: FontWeight.Bold),
Colour = Color4.White,
Padding = new MarginPadding(20)
}
@ -124,10 +120,9 @@ namespace osu.Game.Tournament
using (var sr = new StreamReader(stream))
ladder = JsonConvert.DeserializeObject<LadderInfo>(sr.ReadToEnd());
}
else
{
if (ladder == null)
ladder = new LadderInfo();
}
if (ladder.Ruleset.Value == null)
ladder.Ruleset.Value = RulesetStore.AvailableRulesets.First();
@ -205,9 +200,11 @@ namespace osu.Game.Tournament
{
foreach (var p in t.Players)
{
if (p.Username == null || p.Statistics == null)
if (string.IsNullOrEmpty(p.Username) || p.Statistics == null)
{
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;
}

View File

@ -61,7 +61,7 @@ namespace osu.Game.Tournament
//Masking = true,
Children = new Drawable[]
{
video = new TourneyVideo(storage.GetStream("BG Logoless - OWC.m4v"))
video = new TourneyVideo(storage.GetStream("videos/main.m4v"))
{
Loop = true,
RelativeSizeAxes = Axes.Both,
@ -80,6 +80,7 @@ namespace osu.Game.Tournament
new ShowcaseScreen(),
new MapPoolScreen(),
new TeamIntroScreen(),
new SeedingScreen(),
new DrawingsScreen(),
new GameplayScreen(),
new TeamWinScreen()
@ -121,6 +122,7 @@ namespace osu.Game.Tournament
new ScreenButton(typeof(LadderScreen)) { Text = "Bracket", RequestSelection = SetScreen },
new Separator(),
new ScreenButton(typeof(TeamIntroScreen)) { Text = "TeamIntro", RequestSelection = SetScreen },
new ScreenButton(typeof(SeedingScreen)) { Text = "Seeding", RequestSelection = SetScreen },
new Separator(),
new ScreenButton(typeof(MapPoolScreen)) { Text = "MapPool", RequestSelection = SetScreen },
new ScreenButton(typeof(GameplayScreen)) { Text = "Gameplay", RequestSelection = SetScreen },
@ -146,8 +148,20 @@ namespace osu.Game.Tournament
private Drawable currentScreen;
private ScheduledDelegate scheduledHide;
private Drawable temporaryScreen;
public void SetScreen(Drawable screen)
{
currentScreen?.Hide();
currentScreen = null;
screens.Add(temporaryScreen = screen);
}
public void SetScreen(Type screenType)
{
temporaryScreen?.Expire();
var target = screens.FirstOrDefault(s => s.GetType() == screenType);
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.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Framework.Configuration;
using osu.Framework.Configuration.Tracking;
using osu.Framework.Extensions;
@ -126,6 +127,35 @@ namespace osu.Game.Configuration
public OsuConfigManager(Storage 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

View File

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

View File

@ -6,6 +6,7 @@ using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Layout;
namespace osu.Game.Graphics.Containers
{
@ -142,15 +143,17 @@ namespace osu.Game.Graphics.Containers
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;

View File

@ -17,7 +17,9 @@ namespace osu.Game.Graphics
/// </summary>
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>
/// Retrieves a <see cref="FontUsage"/>.
@ -45,6 +47,9 @@ namespace osu.Game.Graphics
case Typeface.Venera:
return "Venera";
case Typeface.Torus:
return "Torus";
}
return null;
@ -65,16 +70,7 @@ namespace osu.Game.Graphics
/// <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 (family != GetFamilyString(Typeface.Exo) && weight == FontWeight.Regular)
weightString = string.Empty;
return weightString;
}
public static string GetWeightString(string family, FontWeight weight) => weight.ToString();
}
public static class OsuFontExtensions
@ -102,15 +98,39 @@ namespace osu.Game.Graphics
{
Exo,
Venera,
Torus
}
public enum FontWeight
{
Light,
Regular,
Medium,
SemiBold,
Bold,
Black
/// <summary>
/// Equivalent to weight 300.
/// </summary>
Light = 300,
/// <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>
public class ExpandingBar : Circle
{
private bool isCollapsed;
private bool expanded = true;
public bool IsCollapsed
public bool Expanded
{
get => isCollapsed;
get => expanded;
set
{
if (value == isCollapsed)
if (value == expanded)
return;
isCollapsed = value;
expanded = value;
updateState();
}
}
@ -83,19 +83,21 @@ namespace osu.Game.Graphics.UserInterface
updateState();
}
public void Collapse() => IsCollapsed = true;
public void Collapse() => Expanded = false;
public void Expand() => IsCollapsed = false;
public void Expand() => Expanded = true;
private void updateState()
{
float newSize = IsCollapsed ? CollapsedSize : ExpandedSize;
Easing easingType = IsCollapsed ? Easing.Out : Easing.OutElastic;
float newSize = expanded ? ExpandedSize : CollapsedSize;
Easing easingType = expanded ? Easing.OutElastic : Easing.Out;
if (RelativeSizeAxes == Axes.X)
this.ResizeHeightTo(newSize, 400, easingType);
else
this.ResizeWidthTo(newSize, 400, easingType);
this.FadeTo(expanded ? 1 : 0.5f, 100, Easing.OutQuint);
}
}
}

View File

@ -4,11 +4,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Caching;
using osuTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines;
using osu.Framework.Layout;
using osuTK.Graphics;
namespace osu.Game.Graphics.UserInterface
@ -83,17 +83,11 @@ namespace osu.Game.Graphics.UserInterface
PathRadius = 1
}
});
AddLayout(pathCached);
}
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & Invalidation.DrawSize) > 0)
pathCached.Invalidate();
return base.Invalidate(invalidation, source, shallPropagate);
}
private readonly Cached pathCached = new Cached();
private readonly LayoutValue pathCached = new LayoutValue(Invalidation.DrawSize);
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="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>
public DatabasedKeyBindingContainer(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None)
: base(simultaneousMode)
/// <param name="matchingMode">Specify how to deal with exact <see cref="KeyCombination"/> matches.</param>
public DatabasedKeyBindingContainer(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None, KeyCombinationMatchingMode matchingMode = KeyCombinationMatchingMode.Any)
: base(simultaneousMode, matchingMode)
{
this.ruleset = ruleset;
this.variant = variant;

View File

@ -15,6 +15,7 @@ namespace osu.Game.Input.Bindings
private readonly Drawable handler;
public GlobalActionContainer(OsuGameBase game)
: base(matchingMode: KeyCombinationMatchingMode.Modifiers)
{
if (game is IKeyBindingHandler<GlobalAction>)
handler = game;
@ -38,6 +39,9 @@ namespace osu.Game.Input.Bindings
new KeyBinding(InputKey.Escape, 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.Enter, GlobalAction.Select),
new KeyBinding(InputKey.KeypadEnter, GlobalAction.Select),
@ -54,10 +58,11 @@ namespace osu.Game.Input.Bindings
public IEnumerable<KeyBinding> AudioControlKeyBindings => new[]
{
new KeyBinding(InputKey.Up, GlobalAction.IncreaseVolume),
new KeyBinding(InputKey.MouseWheelUp, GlobalAction.IncreaseVolume),
new KeyBinding(InputKey.Down, GlobalAction.DecreaseVolume),
new KeyBinding(InputKey.MouseWheelDown, GlobalAction.DecreaseVolume),
new KeyBinding(new[] { InputKey.Alt, InputKey.Up }, GlobalAction.IncreaseVolume),
new KeyBinding(new[] { InputKey.Alt, InputKey.MouseWheelUp }, GlobalAction.IncreaseVolume),
new KeyBinding(new[] { InputKey.Alt, InputKey.Down }, GlobalAction.DecreaseVolume),
new KeyBinding(new[] { InputKey.Alt, InputKey.MouseWheelDown }, GlobalAction.DecreaseVolume),
new KeyBinding(InputKey.F4, GlobalAction.ToggleMute),
new KeyBinding(InputKey.TrackPrevious, GlobalAction.MusicPrev),
@ -141,5 +146,11 @@ namespace osu.Game.Input.Bindings
[Description("Toggle now playing overlay")]
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")
.ValueGeneratedOnAdd();
b.Property<int>("AudioLeadIn");
b.Property<double>("AudioLeadIn");
b.Property<double>("BPM");

View File

@ -58,7 +58,7 @@ namespace osu.Game.Online.Leaderboards
Spacing = new Vector2(-3, 0),
Padding = new MarginPadding { Top = 5 },
Colour = getRankNameColour(),
Font = OsuFont.GetFont(Typeface.Venera, 25),
Font = OsuFont.Numeric.With(size: 25),
Text = getRankName(),
ShadowColour = Color4.Black.Opacity(0.3f),
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.Input;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Platform;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
@ -42,6 +43,7 @@ using osu.Game.Overlays.Volume;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Screens.Select;
using osu.Game.Updater;
using osu.Game.Utils;
using LogLevel = osu.Framework.Logging.LogLevel;
@ -389,24 +391,35 @@ namespace osu.Game
protected virtual Loader CreateLoader() => new Loader();
protected virtual UpdateManager CreateUpdateManager() => new UpdateManager();
protected override Container CreateScalingContainer() => new ScalingContainer(ScalingMode.Everything);
#region Beatmap progression
private void beatmapChanged(ValueChangedEvent<WorkingBeatmap> beatmap)
{
var nextBeatmap = beatmap.NewValue;
if (nextBeatmap?.Track != null)
nextBeatmap.Track.Completed += currentTrackCompleted;
var oldBeatmap = beatmap.OldValue;
if (oldBeatmap?.Track != null)
oldBeatmap.Track.Completed -= currentTrackCompleted;
beatmap.OldValue?.CancelAsyncLoad();
updateModDefaults();
oldBeatmap?.CancelAsyncLoad();
nextBeatmap?.BeginAsyncLoad();
var newBeatmap = beatmap.NewValue;
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)
@ -427,12 +440,6 @@ namespace osu.Game
}
}
private void currentTrackCompleted() => Schedule(() =>
{
if (!Beatmap.Value.Track.Looping && !Beatmap.Disabled)
musicController.NextTrack();
});
#endregion
private ScheduledDelegate performFromMainMenuTask;
@ -584,7 +591,7 @@ namespace osu.Game
loadComponentSingleFile(new OnScreenDisplay(), Add, true);
loadComponentSingleFile(musicController = new MusicController(), Add, true);
loadComponentSingleFile(MusicController = new MusicController(), Add, true);
loadComponentSingleFile(notifications = new NotificationOverlay
{
@ -627,6 +634,7 @@ namespace osu.Game
chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible;
Add(externalLinkOpener = new ExternalLinkOpener());
Add(CreateUpdateManager()); // dependency on notification overlay
// side overlays which cancel each other.
var singleDisplaySideOverlays = new OverlayContainer[] { Settings, notifications };
@ -805,6 +813,13 @@ namespace osu.Game
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)
{
if (introScreen == null) return false;
@ -885,7 +900,7 @@ namespace osu.Game
private ScalingContainer screenContainer;
private MusicController musicController;
protected MusicController MusicController { get; private set; }
protected override bool OnExiting()
{
@ -943,7 +958,7 @@ namespace osu.Game
{
OverlayActivationMode.Value = newOsuScreen.InitialOverlayActivationMode;
musicController.AllowRateAdjustments = newOsuScreen.AllowRateAdjustments;
MusicController.AllowRateAdjustments = newOsuScreen.AllowRateAdjustments;
if (newOsuScreen.HideOverlaysOnEnter)
CloseAllOverlays();

View File

@ -97,7 +97,7 @@ namespace osu.Game
public bool IsDeployedBuild => AssemblyVersion.Major > 0;
public string Version
public virtual string Version
{
get
{
@ -157,9 +157,14 @@ namespace osu.Game
AddFont(Resources, @"Fonts/Exo2.0-Black");
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-Medium");
AddFont(Resources, @"Fonts/Venera-Bold");
AddFont(Resources, @"Fonts/Venera-Black");
runMigrations();
@ -206,6 +211,10 @@ namespace osu.Game
Audio.Tracks.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8));
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(() =>
{
// 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)
{
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)
{

View File

@ -4,9 +4,11 @@
using System;
using System.Collections.Generic;
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.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses;
@ -22,6 +24,8 @@ namespace osu.Game.Overlays.Changelog
private const string listing_string = "listing";
private Box streamsBackground;
public ChangelogHeader()
{
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 void showBuild(ValueChangedEvent<APIChangelogBuild> e)
@ -72,7 +82,21 @@ namespace osu.Game.Overlays.Changelog
AutoSizeAxes = Axes.Y,
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 Container fadeContainer;
private FillFlowContainer<SpriteText> text;
private ExpandingBar expandingBar;
@ -44,47 +43,39 @@ namespace osu.Game.Overlays.Changelog
AddRange(new Drawable[]
{
fadeContainer = new Container
text = new FillFlowContainer<SpriteText>
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = 6 },
Children = new[]
{
text = new FillFlowContainer<SpriteText>
new OsuSpriteText
{
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
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
},
}
Text = stream.DisplayName,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Black),
},
expandingBar = new ExpandingBar
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Colour = stream.Colour,
ExpandedSize = 4,
CollapsedSize = 2,
IsCollapsed = true
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
{
Anchor = Anchor.TopCentre,
Colour = stream.Colour,
ExpandedSize = 4,
CollapsedSize = 2,
Expanded = true
},
new HoverClickSounds()
});
@ -109,38 +100,41 @@ namespace osu.Game.Overlays.Changelog
private void updateState()
{
// Expand based on the local state
bool shouldExpand = Active.Value || IsHovered;
// highlighted regardless if we are hovered
bool textHighlighted = IsHovered;
bool barExpanded = IsHovered;
// Expand based on whether no build is selected and the badge area is hovered
shouldExpand |= SelectedTab.Value == null && !externalDimRequested;
if (shouldExpand)
if (SelectedTab.Value == null)
{
expandingBar.Expand();
fadeContainer.FadeTo(1, transition_duration);
// at listing, all badges are highlighted when user is not hovering any badge.
textHighlighted |= !userHoveringArea;
barExpanded |= !userHoveringArea;
}
else
{
expandingBar.Collapse();
fadeContainer.FadeTo(0.5f, transition_duration);
// bar is always expanded when active
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;
updateState();
}
set
{
if (value == userHoveringArea)
return;
public void DisableDim()
{
externalDimRequested = false;
updateState();
userHoveringArea = value;
updateState();
}
}
}
}

View File

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

View File

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

View File

@ -14,8 +14,25 @@ namespace osu.Game.Overlays.Volume
public Func<GlobalAction, bool> ActionRequested;
public Func<GlobalAction, float, bool, bool> ScrollActionRequested;
public bool OnPressed(GlobalAction action) => ActionRequested?.Invoke(action) ?? false;
public bool OnScroll(GlobalAction action, float amount, bool isPrecise) => ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false;
public bool OnPressed(GlobalAction action)
{
// 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)
{

View File

@ -11,16 +11,18 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Utils;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Input.Bindings;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.Volume
{
public class VolumeMeter : Container
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
{
private CircularProgress volumeCircle;
private CircularProgress volumeCircleGlow;
@ -260,5 +262,28 @@ namespace osu.Game.Overlays.Volume
{
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.Caching;
using osu.Framework.Graphics;
using osu.Framework.Layout;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
@ -19,11 +20,13 @@ namespace osu.Game.Rulesets.UI.Scrolling
[Resolved]
private IScrollingInfo scrollingInfo { get; set; }
private readonly Cached initialStateCache = new Cached();
private readonly LayoutValue initialStateCache = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo);
public ScrollingHitObjectContainer()
{
RelativeSizeAxes = Axes.Both;
AddLayout(initialStateCache);
}
[BackgroundDependencyLoader]
@ -55,14 +58,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
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;
protected override void Update()

View File

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

View File

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

View File

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

View File

@ -123,6 +123,10 @@ namespace osu.Game.Screens.Play
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(() =>
{
track.Reset();

View File

@ -12,7 +12,6 @@ using osu.Game.Graphics;
using osu.Framework.Allocation;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Shapes;
using osuTK.Input;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Input.Bindings;
@ -204,35 +203,24 @@ namespace osu.Game.Screens.Play
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)
{
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:
BackAction.Invoke();
return true;

View File

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

View File

@ -16,15 +16,17 @@ using osu.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Threading;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor;
using osu.Game.Input.Bindings;
using osu.Game.Screens.Select.Carousel;
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_bottom = Footer.HEIGHT;
@ -435,41 +437,38 @@ namespace osu.Game.Screens.Select
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)
{
case Key.Up:
direction = -1;
break;
case Key.Down:
direction = 1;
break;
case Key.Left:
direction = -1;
skipDifficulties = true;
break;
SelectNext(-1, true);
return true;
case Key.Right:
direction = 1;
skipDifficulties = true;
break;
SelectNext(1, true);
return true;
}
if (direction == 0)
return base.OnKeyDown(e);
return false;
}
SelectNext(direction, skipDifficulties);
return true;
public bool OnPressed(GlobalAction action)
{
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()

View File

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

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