diff --git a/osu.Android.props b/osu.Android.props
index 6a8e66ee6a..f623a92ade 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,6 +52,6 @@
-
+
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs
index 20911b8d06..024c4cefb0 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs
@@ -18,7 +18,9 @@ namespace osu.Game.Rulesets.Catch.Tests
public override IReadOnlyList RequiredTypes => new[]
{
typeof(BananaShower),
+ typeof(Banana),
typeof(DrawableBananaShower),
+ typeof(DrawableBanana),
typeof(CatchRuleset),
typeof(DrawableCatchRuleset),
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
index cf4843c200..cf68c5424d 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
@@ -8,9 +8,15 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Catch.Beatmaps;
+using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Rulesets.UI;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Catch.Tests
@@ -27,9 +33,41 @@ namespace osu.Game.Rulesets.Catch.Tests
CreatedDrawables.OfType().Select(i => i.Child)
.OfType().ForEach(c => c.ToggleHyperDash(t)));
- AddRepeatStep("catch fruit", () =>
- this.ChildrenOfType().ForEach(area =>
- area.MovableCatcher.PlaceOnPlate(new DrawableFruit(new TestSceneFruitObjects.TestCatchFruit(FruitVisualRepresentation.Grape)))), 20);
+ AddRepeatStep("catch fruit", () => catchFruit(new TestFruit(false)
+ {
+ X = this.ChildrenOfType().First().MovableCatcher.X
+ }), 20);
+ AddRepeatStep("catch fruit last in combo", () => catchFruit(new TestFruit(false)
+ {
+ X = this.ChildrenOfType().First().MovableCatcher.X,
+ LastInCombo = true,
+ }), 20);
+ AddRepeatStep("catch kiai fruit", () => catchFruit(new TestFruit(true)
+ {
+ X = this.ChildrenOfType().First().MovableCatcher.X,
+ }), 20);
+ AddRepeatStep("miss fruit", () => catchFruit(new Fruit
+ {
+ X = this.ChildrenOfType().First().MovableCatcher.X + 100,
+ LastInCombo = true,
+ }, true), 20);
+ }
+
+ private void catchFruit(Fruit fruit, bool miss = false)
+ {
+ this.ChildrenOfType().ForEach(area =>
+ {
+ DrawableFruit drawable = new DrawableFruit(fruit);
+ area.Add(drawable);
+
+ Schedule(() =>
+ {
+ area.AttemptCatch(fruit);
+ area.OnResult(drawable, new JudgementResult(fruit, new CatchJudgement()) { Type = miss ? HitResult.Miss : HitResult.Great });
+
+ drawable.Expire();
+ });
+ });
}
private void createCatcher(float size)
@@ -40,7 +78,8 @@ namespace osu.Game.Rulesets.Catch.Tests
Child = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size })
{
Anchor = Anchor.CentreLeft,
- Origin = Anchor.TopLeft
+ Origin = Anchor.TopLeft,
+ CreateDrawableRepresentation = ((DrawableRuleset)catchRuleset.CreateInstance().CreateDrawableRulesetWith(new CatchBeatmap())).CreateDrawableRepresentation
},
});
}
@@ -51,6 +90,17 @@ namespace osu.Game.Rulesets.Catch.Tests
catchRuleset = rulesets.GetRuleset(2);
}
+ public class TestFruit : Fruit
+ {
+ public TestFruit(bool kiai)
+ {
+ var kiaiCpi = new ControlPointInfo();
+ kiaiCpi.Add(0, new EffectControlPoint { KiaiMode = kiai });
+
+ ApplyDefaultsToSelf(kiaiCpi, new BeatmapDifficulty());
+ }
+ }
+
private class TestCatcherArea : CatcherArea
{
public TestCatcherArea(BeatmapDifficulty beatmapDifficulty)
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs
index 070847c0c1..304c7e3854 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs
@@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using osu.Framework.Allocation;
+using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
@@ -34,8 +34,8 @@ namespace osu.Game.Rulesets.Catch.Tests
private DrawableCatchRuleset drawableRuleset;
private double playfieldTime => drawableRuleset.Playfield.Time.Current;
- [BackgroundDependencyLoader]
- private void load()
+ [SetUp]
+ public void Setup() => Schedule(() =>
{
var controlPointInfo = new ControlPointInfo();
controlPointInfo.Add(0, new TimingControlPoint());
@@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Catch.Tests
ControlPointInfo = controlPointInfo
});
- Add(new Container
+ Child = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -66,16 +66,49 @@ namespace osu.Game.Rulesets.Catch.Tests
{
drawableRuleset = new DrawableCatchRuleset(new CatchRuleset(), beatmap.GetPlayableBeatmap(new CatchRuleset().RulesetInfo))
}
- });
+ };
+ });
+
+ [Test]
+ public void TestFruits()
+ {
+ AddStep("hit fruits", () => spawnFruits(true));
+ AddUntilStep("wait for completion", () => playfieldIsEmpty);
+ AddAssert("catcher state is idle", () => catcherState == CatcherAnimationState.Idle);
AddStep("miss fruits", () => spawnFruits());
- AddStep("hit fruits", () => spawnFruits(true));
- AddStep("miss juicestream", () => spawnJuiceStream());
- AddStep("hit juicestream", () => spawnJuiceStream(true));
- AddStep("miss bananas", () => spawnBananas());
- AddStep("hit bananas", () => spawnBananas(true));
+ AddUntilStep("wait for completion", () => playfieldIsEmpty);
+ AddAssert("catcher state is failed", () => catcherState == CatcherAnimationState.Fail);
}
+ [Test]
+ public void TestJuicestream()
+ {
+ AddStep("hit juicestream", () => spawnJuiceStream(true));
+ AddUntilStep("wait for completion", () => playfieldIsEmpty);
+ AddAssert("catcher state is idle", () => catcherState == CatcherAnimationState.Idle);
+
+ AddStep("miss juicestream", () => spawnJuiceStream());
+ AddUntilStep("wait for completion", () => playfieldIsEmpty);
+ AddAssert("catcher state is failed", () => catcherState == CatcherAnimationState.Fail);
+ }
+
+ [Test]
+ public void TestBananas()
+ {
+ AddStep("hit bananas", () => spawnBananas(true));
+ AddUntilStep("wait for completion", () => playfieldIsEmpty);
+ AddAssert("catcher state is idle", () => catcherState == CatcherAnimationState.Idle);
+
+ AddStep("miss bananas", () => spawnBananas());
+ AddUntilStep("wait for completion", () => playfieldIsEmpty);
+ AddAssert("catcher state is idle", () => catcherState == CatcherAnimationState.Idle);
+ }
+
+ private bool playfieldIsEmpty => !((CatchPlayfield)drawableRuleset.Playfield).AllHitObjects.Any(h => h.IsAlive);
+
+ private CatcherAnimationState catcherState => ((CatchPlayfield)drawableRuleset.Playfield).CatcherArea.MovableCatcher.CurrentState;
+
private void spawnFruits(bool hit = false)
{
for (int i = 1; i <= 4; i++)
diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
index 1a5d0f983b..986dc9dbb9 100644
--- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
@@ -28,8 +28,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
ApplyPositionOffsets(Beatmap);
- initialiseHyperDash((List)Beatmap.HitObjects);
-
int index = 0;
foreach (var obj in Beatmap.HitObjects.OfType())
@@ -76,6 +74,12 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
break;
case JuiceStream juiceStream:
+ // Todo: BUG!! Stable used the last control point as the final position of the path, but it should use the computed path instead.
+ lastPosition = juiceStream.X + juiceStream.Path.ControlPoints[^1].Position.Value.X / CatchPlayfield.BASE_WIDTH;
+
+ // Todo: BUG!! Stable attempted to use the end time of the stream, but referenced it too early in execution and used the start time instead.
+ lastStartTime = juiceStream.StartTime;
+
foreach (var nested in juiceStream.NestedHitObjects)
{
var catchObject = (CatchHitObject)nested;
@@ -90,20 +94,12 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
break;
}
}
+
+ initialiseHyperDash(beatmap);
}
private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng)
{
- if (hitObject is JuiceStream stream)
- {
- lastPosition = stream.EndX;
- lastStartTime = stream.EndTime;
- return;
- }
-
- if (!(hitObject is Fruit))
- return;
-
float offsetPosition = hitObject.X;
double startTime = hitObject.StartTime;
@@ -116,7 +112,9 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
}
float positionDiff = offsetPosition - lastPosition.Value;
- double timeDiff = startTime - lastStartTime;
+
+ // Todo: BUG!! Stable calculated time deltas as ints, which affects randomisation. This should be changed to a double.
+ int timeDiff = (int)(startTime - lastStartTime);
if (timeDiff > 1000)
{
@@ -132,7 +130,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
return;
}
- if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3d)
+ // ReSharper disable once PossibleLossOfFraction
+ if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3)
applyOffset(ref offsetPosition, positionDiff);
hitObject.XOffset = offsetPosition - hitObject.X;
@@ -191,14 +190,14 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
}
}
- private void initialiseHyperDash(List objects)
+ private static void initialiseHyperDash(IBeatmap beatmap)
{
List objectWithDroplets = new List();
- foreach (var currentObject in objects)
+ foreach (var currentObject in beatmap.HitObjects)
{
- if (currentObject is Fruit)
- objectWithDroplets.Add(currentObject);
+ if (currentObject is Fruit fruitObject)
+ objectWithDroplets.Add(fruitObject);
if (currentObject is JuiceStream)
{
@@ -212,7 +211,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
objectWithDroplets.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime));
- double halfCatcherWidth = CatcherArea.GetCatcherSize(Beatmap.BeatmapInfo.BaseDifficulty) / 2;
+ double halfCatcherWidth = CatcherArea.GetCatcherSize(beatmap.BeatmapInfo.BaseDifficulty) / 2;
int lastDirection = 0;
double lastExcess = halfCatcherWidth;
@@ -221,6 +220,10 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
CatchHitObject currentObject = objectWithDroplets[i];
CatchHitObject nextObject = objectWithDroplets[i + 1];
+ // Reset variables in-case values have changed (e.g. after applying HR)
+ currentObject.HyperDashTarget = null;
+ currentObject.DistanceToHyperDash = 0;
+
int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
diff --git a/osu.Game.Rulesets.Catch/CatchSkinComponents.cs b/osu.Game.Rulesets.Catch/CatchSkinComponents.cs
index 08bff36401..80390705fe 100644
--- a/osu.Game.Rulesets.Catch/CatchSkinComponents.cs
+++ b/osu.Game.Rulesets.Catch/CatchSkinComponents.cs
@@ -11,6 +11,8 @@ namespace osu.Game.Rulesets.Catch
FruitOrange,
FruitPear,
Droplet,
- CatcherIdle
+ CatcherIdle,
+ CatcherFail,
+ CatcherKiai
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs
index cf7231ebb2..01b76ceed9 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
+using osu.Framework.Graphics;
using osu.Framework.Utils;
using osuTK.Graphics;
@@ -22,6 +23,23 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
return colour ??= getBananaColour();
}
+ protected override void UpdateInitialTransforms()
+ {
+ base.UpdateInitialTransforms();
+
+ const float end_scale = 0.6f;
+ const float random_scale_range = 1.6f;
+
+ ScaleContainer.ScaleTo(HitObject.Scale * (end_scale + random_scale_range * RNG.NextSingle()))
+ .Then().ScaleTo(HitObject.Scale * end_scale, HitObject.TimePreempt);
+
+ ScaleContainer.RotateTo(getRandomAngle())
+ .Then()
+ .RotateTo(getRandomAngle(), HitObject.TimePreempt);
+
+ float getRandomAngle() => 180 * (RNG.NextSingle() * 2 - 1);
+ }
+
private Color4 getBananaColour()
{
switch (RNG.Next(0, 3))
diff --git a/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs
index af7c60b929..65e6e6f209 100644
--- a/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs
+++ b/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs
@@ -48,6 +48,14 @@ namespace osu.Game.Rulesets.Catch.Skinning
case CatchSkinComponents.CatcherIdle:
return this.GetAnimation("fruit-catcher-idle", true, true, true) ??
this.GetAnimation("fruit-ryuuta", true, true, true);
+
+ case CatchSkinComponents.CatcherFail:
+ return this.GetAnimation("fruit-catcher-fail", true, true, true) ??
+ this.GetAnimation("fruit-ryuuta", true, true, true);
+
+ case CatchSkinComponents.CatcherKiai:
+ return this.GetAnimation("fruit-catcher-kiai", true, true, true) ??
+ this.GetAnimation("fruit-ryuuta", true, true, true);
}
return null;
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherAnimationState.cs b/osu.Game.Rulesets.Catch/UI/CatcherAnimationState.cs
new file mode 100644
index 0000000000..566e9d1911
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/UI/CatcherAnimationState.cs
@@ -0,0 +1,12 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Game.Rulesets.Catch.UI
+{
+ public enum CatcherAnimationState
+ {
+ Idle,
+ Fail,
+ Kiai
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index cc0f41a14f..2394110165 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -6,6 +6,7 @@ using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Input.Bindings;
@@ -148,18 +149,62 @@ namespace osu.Game.Rulesets.Catch.UI
[BackgroundDependencyLoader]
private void load()
{
- Children = new[]
+ Children = new Drawable[]
{
caughtFruit = new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre,
},
- createCatcherSprite().With(c =>
+ catcherIdle = new CatcherSprite(CatcherAnimationState.Idle)
{
- c.Anchor = Anchor.TopCentre;
- })
+ Anchor = Anchor.TopCentre,
+ Alpha = 0,
+ },
+ catcherKiai = new CatcherSprite(CatcherAnimationState.Kiai)
+ {
+ Anchor = Anchor.TopCentre,
+ Alpha = 0,
+ },
+ catcherFail = new CatcherSprite(CatcherAnimationState.Fail)
+ {
+ Anchor = Anchor.TopCentre,
+ Alpha = 0,
+ }
};
+
+ updateCatcher();
+ }
+
+ private CatcherSprite catcherIdle;
+ private CatcherSprite catcherKiai;
+ private CatcherSprite catcherFail;
+
+ private void updateCatcher()
+ {
+ catcherIdle.Hide();
+ catcherKiai.Hide();
+ catcherFail.Hide();
+
+ CatcherSprite current;
+
+ switch (CurrentState)
+ {
+ default:
+ current = catcherIdle;
+ break;
+
+ case CatcherAnimationState.Fail:
+ current = catcherFail;
+ break;
+
+ case CatcherAnimationState.Kiai:
+ current = catcherKiai;
+ break;
+ }
+
+ current.Show();
+ (current.Drawable as IAnimation)?.GotoFrame(0);
}
private int currentDirection;
@@ -229,7 +274,7 @@ namespace osu.Game.Rulesets.Catch.UI
return additive;
}
- private Drawable createCatcherSprite() => new CatcherSprite();
+ private Drawable createCatcherSprite() => new CatcherSprite(CurrentState);
///
/// Add a caught fruit to the catcher's stack.
@@ -297,9 +342,25 @@ namespace osu.Game.Rulesets.Catch.UI
SetHyperDashState();
}
+ if (validCatch)
+ updateState(fruit.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle);
+ else if (!(fruit is Banana))
+ updateState(CatcherAnimationState.Fail);
+
return validCatch;
}
+ private void updateState(CatcherAnimationState state)
+ {
+ if (CurrentState == state)
+ return;
+
+ CurrentState = state;
+ updateCatcher();
+ }
+
+ public CatcherAnimationState CurrentState { get; private set; }
+
private double hyperDashModifier = 1;
private int hyperDashDirection;
private float hyperDashTargetPosition;
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs b/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs
index 78020114cd..52eb8d597e 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs
@@ -14,9 +14,9 @@ namespace osu.Game.Rulesets.Catch.UI
{
protected override bool ApplySizeRestrictionsToDefault => true;
- public CatcherSprite()
- : base(new CatchSkinComponent(CatchSkinComponents.CatcherIdle), _ =>
- new DefaultCatcherSprite(), confineMode: ConfineMode.ScaleDownToFit)
+ public CatcherSprite(CatcherAnimationState state)
+ : base(new CatchSkinComponent(componentFromState(state)), _ =>
+ new DefaultCatcherSprite(state), confineMode: ConfineMode.ScaleDownToFit)
{
RelativeSizeAxes = Axes.None;
Size = new Vector2(CatcherArea.CATCHER_SIZE);
@@ -25,12 +25,34 @@ namespace osu.Game.Rulesets.Catch.UI
OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE;
}
+ private static CatchSkinComponents componentFromState(CatcherAnimationState state)
+ {
+ switch (state)
+ {
+ case CatcherAnimationState.Fail:
+ return CatchSkinComponents.CatcherFail;
+
+ case CatcherAnimationState.Kiai:
+ return CatchSkinComponents.CatcherKiai;
+
+ default:
+ return CatchSkinComponents.CatcherIdle;
+ }
+ }
+
private class DefaultCatcherSprite : Sprite
{
+ private readonly CatcherAnimationState state;
+
+ public DefaultCatcherSprite(CatcherAnimationState state)
+ {
+ this.state = state;
+ }
+
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
- Texture = textures.Get("Gameplay/catch/fruit-catcher-idle");
+ Texture = textures.Get($"Gameplay/catch/fruit-catcher-{state.ToString().ToLower()}");
}
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
index 7a8570c09b..864fd31a0f 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
@@ -17,8 +17,8 @@ namespace osu.Game.Tests.Visual.Online
public override IReadOnlyList RequiredTypes => new[]
{
- typeof(UpdateStreamBadgeArea),
- typeof(UpdateStreamBadge),
+ typeof(ChangelogUpdateStreamControl),
+ typeof(ChangelogUpdateStreamItem),
typeof(ChangelogHeader),
typeof(ChangelogContent),
typeof(ChangelogListing),
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs
index 71ae47dc66..80e03d82e2 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs
@@ -497,7 +497,7 @@ namespace osu.Game.Tests.Visual.SongSelect
}
bool changed = false;
- AddStep($"Load {beatmapSets.Count} Beatmaps", () =>
+ AddStep($"Load {(beatmapSets.Count > 0 ? beatmapSets.Count.ToString() : "some")} beatmaps", () =>
{
carousel.Filter(new FilterCriteria());
carousel.BeatmapSetsChanged = () => changed = true;
@@ -697,6 +697,8 @@ namespace osu.Game.Tests.Visual.SongSelect
public new List Items => base.Items;
public bool PendingFilterTask => PendingFilter != null;
+
+ protected override IEnumerable GetLoadableBeatmaps() => Enumerable.Empty();
}
}
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFriendsOnlineStatusControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFriendsOnlineStatusControl.cs
new file mode 100644
index 0000000000..0d841dfef1
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFriendsOnlineStatusControl.cs
@@ -0,0 +1,63 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Overlays;
+using osu.Game.Overlays.Home.Friends;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ public class TestSceneFriendsOnlineStatusControl : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(FriendsOnlineStatusControl),
+ typeof(FriendsOnlineStatusItem),
+ typeof(OverlayStreamControl<>),
+ typeof(OverlayStreamItem<>),
+ typeof(FriendsBundle)
+ };
+
+ [Cached]
+ private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
+
+ private FriendsOnlineStatusControl control;
+
+ [SetUp]
+ public void SetUp() => Schedule(() => Child = control = new FriendsOnlineStatusControl
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ });
+
+ [Test]
+ public void Populate()
+ {
+ AddStep("Populate", () => control.Populate(new List
+ {
+ new User
+ {
+ IsOnline = true
+ },
+ new User
+ {
+ IsOnline = false
+ },
+ new User
+ {
+ IsOnline = false
+ }
+ }));
+
+ AddAssert("3 users", () => control.Items.FirstOrDefault(item => item.Status == FriendsOnlineStatus.All)?.Count == 3);
+ AddAssert("1 online user", () => control.Items.FirstOrDefault(item => item.Status == FriendsOnlineStatus.Online)?.Count == 1);
+ AddAssert("2 offline users", () => control.Items.FirstOrDefault(item => item.Status == FriendsOnlineStatus.Offline)?.Count == 2);
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.cs b/osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.cs
new file mode 100644
index 0000000000..9f885ed827
--- /dev/null
+++ b/osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.cs
@@ -0,0 +1,42 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics;
+using osu.Game.Tournament.Components;
+using osu.Game.Tournament.Screens.Gameplay.Components;
+using osuTK;
+
+namespace osu.Game.Tournament.Tests.Components
+{
+ public class TestSceneMatchHeader : TournamentTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DrawableTournamentHeaderText),
+ typeof(DrawableTournamentHeaderLogo),
+ };
+
+ public TestSceneMatchHeader()
+ {
+ Child = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(50),
+ Children = new Drawable[]
+ {
+ new TournamentSpriteText { Text = "with logo", Font = OsuFont.Torus.With(size: 30) },
+ new MatchHeader(),
+ new TournamentSpriteText { Text = "without logo", Font = OsuFont.Torus.With(size: 30) },
+ new MatchHeader { ShowLogo = false },
+ new TournamentSpriteText { Text = "without scores", Font = OsuFont.Torus.With(size: 30) },
+ new MatchHeader { ShowScores = false },
+ }
+ };
+ }
+ }
+}
diff --git a/osu.Game.Tournament/Components/DrawableTournamentHeaderLogo.cs b/osu.Game.Tournament/Components/DrawableTournamentHeaderLogo.cs
new file mode 100644
index 0000000000..3f5ab42fd7
--- /dev/null
+++ b/osu.Game.Tournament/Components/DrawableTournamentHeaderLogo.cs
@@ -0,0 +1,37 @@
+// Copyright (c) ppy Pty Ltd . 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.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+
+namespace osu.Game.Tournament.Components
+{
+ public class DrawableTournamentHeaderLogo : CompositeDrawable
+ {
+ public DrawableTournamentHeaderLogo()
+ {
+ InternalChild = new LogoSprite();
+
+ Height = 82;
+ RelativeSizeAxes = Axes.X;
+ }
+
+ private class LogoSprite : Sprite
+ {
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ RelativeSizeAxes = Axes.Both;
+ FillMode = FillMode.Fit;
+
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+
+ Texture = textures.Get("header-logo");
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs b/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs
new file mode 100644
index 0000000000..bda696ba00
--- /dev/null
+++ b/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs
@@ -0,0 +1,37 @@
+// Copyright (c) ppy Pty Ltd . 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.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+
+namespace osu.Game.Tournament.Components
+{
+ public class DrawableTournamentHeaderText : CompositeDrawable
+ {
+ public DrawableTournamentHeaderText()
+ {
+ InternalChild = new TextSprite();
+
+ Height = 22;
+ RelativeSizeAxes = Axes.X;
+ }
+
+ private class TextSprite : Sprite
+ {
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ RelativeSizeAxes = Axes.Both;
+ FillMode = FillMode.Fit;
+
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+
+ Texture = textures.Get("header-text");
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tournament/Components/DrawableTournamentTitleText.cs b/osu.Game.Tournament/Components/DrawableTournamentTitleText.cs
deleted file mode 100644
index 4fbc6cd060..0000000000
--- a/osu.Game.Tournament/Components/DrawableTournamentTitleText.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using osu.Game.Graphics;
-
-namespace osu.Game.Tournament.Components
-{
- public class DrawableTournamentTitleText : TournamentSpriteText
- {
- public DrawableTournamentTitleText()
- {
- Text = "osu!taiko world cup 2020";
- Font = OsuFont.Torus.With(size: 26, weight: FontWeight.SemiBold);
- }
- }
-}
diff --git a/osu.Game.Tournament/Components/RoundDisplay.cs b/osu.Game.Tournament/Components/RoundDisplay.cs
index dd56c83c57..bebede6782 100644
--- a/osu.Game.Tournament/Components/RoundDisplay.cs
+++ b/osu.Game.Tournament/Components/RoundDisplay.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Tournament.Components
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- new DrawableTournamentTitleText(),
+ new DrawableTournamentHeaderText(),
new TournamentSpriteText
{
Text = match.Round.Value?.Name.Value ?? "Unknown Round",
diff --git a/osu.Game.Tournament/Components/TourneyVideo.cs b/osu.Game.Tournament/Components/TourneyVideo.cs
index 43088d6b92..bc66fad8c1 100644
--- a/osu.Game.Tournament/Components/TourneyVideo.cs
+++ b/osu.Game.Tournament/Components/TourneyVideo.cs
@@ -7,7 +7,6 @@ using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Video;
-using osu.Framework.Platform;
using osu.Framework.Timing;
using osu.Game.Graphics;
@@ -28,13 +27,13 @@ namespace osu.Game.Tournament.Components
}
[BackgroundDependencyLoader]
- private void load(Storage storage)
+ private void load(TournamentStorage storage)
{
- var stream = storage.GetStream($@"videos/{filename}.m4v");
+ var stream = storage.GetStream($@"videos/{filename}");
if (stream != null)
{
- InternalChild = video = new VideoSprite(stream)
+ InternalChild = video = new VideoSprite(stream, false)
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs
index b19f2bedf0..eefa9fcfe6 100644
--- a/osu.Game.Tournament/IPC/FileBasedIPC.cs
+++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs
@@ -163,12 +163,7 @@ namespace osu.Game.Tournament.IPC
{
try
{
- stableInstallPath = "G:\\My Drive\\Main\\osu!tourney";
-
- if (checkExists(stableInstallPath))
- return stableInstallPath;
-
- stableInstallPath = "G:\\My Drive\\Main\\osu!mappool";
+ stableInstallPath = Environment.GetEnvironmentVariable("OSU_STABLE_PATH");
if (checkExists(stableInstallPath))
return stableInstallPath;
diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs
index 69a68c946b..d790f4b754 100644
--- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs
+++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs
@@ -2,14 +2,11 @@
// 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.Input.Events;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
using osuTK;
-using osuTK.Input;
namespace osu.Game.Tournament.Screens.Gameplay.Components
{
@@ -17,13 +14,39 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
{
private TeamScoreDisplay teamDisplay1;
private TeamScoreDisplay teamDisplay2;
+ private DrawableTournamentHeaderLogo logo;
+
+ private bool showScores = true;
public bool ShowScores
{
+ get => showScores;
set
{
- teamDisplay1.ShowScore = value;
- teamDisplay2.ShowScore = value;
+ if (value == showScores)
+ return;
+
+ showScores = value;
+
+ if (IsLoaded)
+ updateDisplay();
+ }
+ }
+
+ private bool showLogo = true;
+
+ public bool ShowLogo
+ {
+ get => showLogo;
+ set
+ {
+ if (value == showLogo)
+ return;
+
+ showLogo = value;
+
+ if (IsLoaded)
+ updateDisplay();
}
}
@@ -38,19 +61,25 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
+ Padding = new MarginPadding(20),
Spacing = new Vector2(5),
Children = new Drawable[]
{
- new DrawableTournamentTitleText
+ logo = new DrawableTournamentHeaderLogo
{
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Scale = new Vector2(1.2f)
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Alpha = showLogo ? 1 : 0
},
- new RoundDisplay
+ new DrawableTournamentHeaderText
{
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ },
+ new MatchRoundDisplay
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
Scale = new Vector2(0.4f)
},
}
@@ -66,76 +95,16 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
Origin = Anchor.TopRight,
},
};
- }
- }
- public class TeamScoreDisplay : CompositeDrawable
- {
- private readonly TeamColour teamColour;
-
- private readonly Bindable currentMatch = new Bindable();
- private readonly Bindable currentTeam = new Bindable();
- private readonly Bindable currentTeamScore = new Bindable();
-
- private TeamDisplay teamDisplay;
-
- public bool ShowScore { set => teamDisplay.ShowScore = value; }
-
- public TeamScoreDisplay(TeamColour teamColour)
- {
- this.teamColour = teamColour;
-
- RelativeSizeAxes = Axes.Y;
- AutoSizeAxes = Axes.X;
+ updateDisplay();
}
- [BackgroundDependencyLoader]
- private void load(LadderInfo ladder)
+ private void updateDisplay()
{
- currentMatch.BindTo(ladder.CurrentMatch);
- currentMatch.BindValueChanged(matchChanged, true);
- }
+ teamDisplay1.ShowScore = showScores;
+ teamDisplay2.ShowScore = showScores;
- private void matchChanged(ValueChangedEvent match)
- {
- currentTeamScore.UnbindBindings();
- currentTeam.UnbindBindings();
-
- if (match.NewValue != null)
- {
- currentTeamScore.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1Score : match.NewValue.Team2Score);
- currentTeam.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1 : match.NewValue.Team2);
- }
-
- // team may change to same team, which means score is not in a good state.
- // thus we handle this manually.
- teamChanged(currentTeam.Value);
- }
-
- protected override bool OnMouseDown(MouseDownEvent e)
- {
- switch (e.Button)
- {
- case MouseButton.Left:
- if (currentTeamScore.Value < currentMatch.Value.PointsToWin)
- currentTeamScore.Value++;
- return true;
-
- case MouseButton.Right:
- if (currentTeamScore.Value > 0)
- currentTeamScore.Value--;
- return true;
- }
-
- return base.OnMouseDown(e);
- }
-
- private void teamChanged(TournamentTeam team)
- {
- InternalChildren = new Drawable[]
- {
- teamDisplay = new TeamDisplay(team, teamColour, currentTeamScore, currentMatch.Value?.PointsToWin ?? 0),
- };
+ logo.Alpha = showLogo ? 1 : 0;
}
}
}
diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/RoundDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchRoundDisplay.cs
similarity index 92%
rename from osu.Game.Tournament/Screens/Gameplay/Components/RoundDisplay.cs
rename to osu.Game.Tournament/Screens/Gameplay/Components/MatchRoundDisplay.cs
index c8b0d3bdda..87793f7e1b 100644
--- a/osu.Game.Tournament/Screens/Gameplay/Components/RoundDisplay.cs
+++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchRoundDisplay.cs
@@ -8,7 +8,7 @@ using osu.Game.Tournament.Models;
namespace osu.Game.Tournament.Screens.Gameplay.Components
{
- public class RoundDisplay : TournamentSpriteTextWithBackground
+ public class MatchRoundDisplay : TournamentSpriteTextWithBackground
{
private readonly Bindable currentMatch = new Bindable();
diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs
index ed14956793..2e7484542a 100644
--- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs
+++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs
@@ -11,6 +11,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Tournament.IPC;
using osu.Game.Tournament.Models;
+using osuTK;
namespace osu.Game.Tournament.Screens.Gameplay.Components
{
@@ -131,13 +132,15 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
Margin = new MarginPadding { Top = bar_height, Horizontal = 10 };
Winning = false;
+
+ DisplayedCountSpriteText.Spacing = new Vector2(-6);
}
public bool Winning
{
set => DisplayedCountSpriteText.Font = value
- ? OsuFont.Torus.With(weight: FontWeight.Bold, size: 50)
- : OsuFont.Torus.With(weight: FontWeight.Regular, size: 40);
+ ? OsuFont.Torus.With(weight: FontWeight.Bold, size: 50, fixedWidth: true)
+ : OsuFont.Torus.With(weight: FontWeight.Regular, size: 40, fixedWidth: true);
}
}
}
diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs
new file mode 100644
index 0000000000..462015f004
--- /dev/null
+++ b/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs
@@ -0,0 +1,83 @@
+// Copyright (c) ppy Pty Ltd . 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.Input.Events;
+using osu.Game.Tournament.Models;
+using osuTK.Input;
+
+namespace osu.Game.Tournament.Screens.Gameplay.Components
+{
+ public class TeamScoreDisplay : CompositeDrawable
+ {
+ private readonly TeamColour teamColour;
+
+ private readonly Bindable currentMatch = new Bindable();
+ private readonly Bindable currentTeam = new Bindable();
+ private readonly Bindable currentTeamScore = new Bindable();
+
+ private TeamDisplay teamDisplay;
+
+ public bool ShowScore { set => teamDisplay.ShowScore = value; }
+
+ public TeamScoreDisplay(TeamColour teamColour)
+ {
+ this.teamColour = teamColour;
+
+ RelativeSizeAxes = Axes.Y;
+ AutoSizeAxes = Axes.X;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(LadderInfo ladder)
+ {
+ currentMatch.BindTo(ladder.CurrentMatch);
+ currentMatch.BindValueChanged(matchChanged, true);
+ }
+
+ private void matchChanged(ValueChangedEvent match)
+ {
+ currentTeamScore.UnbindBindings();
+ currentTeam.UnbindBindings();
+
+ if (match.NewValue != null)
+ {
+ currentTeamScore.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1Score : match.NewValue.Team2Score);
+ currentTeam.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1 : match.NewValue.Team2);
+ }
+
+ // team may change to same team, which means score is not in a good state.
+ // thus we handle this manually.
+ teamChanged(currentTeam.Value);
+ }
+
+ protected override bool OnMouseDown(MouseDownEvent e)
+ {
+ switch (e.Button)
+ {
+ case MouseButton.Left:
+ if (currentTeamScore.Value < currentMatch.Value.PointsToWin)
+ currentTeamScore.Value++;
+ return true;
+
+ case MouseButton.Right:
+ if (currentTeamScore.Value > 0)
+ currentTeamScore.Value--;
+ return true;
+ }
+
+ return base.OnMouseDown(e);
+ }
+
+ private void teamChanged(TournamentTeam team)
+ {
+ InternalChildren = new Drawable[]
+ {
+ teamDisplay = new TeamDisplay(team, teamColour, currentTeamScore, currentMatch.Value?.PointsToWin ?? 0),
+ };
+ }
+ }
+}
diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs
index 4d770855cd..8920990d1b 100644
--- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs
+++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs
@@ -47,7 +47,10 @@ namespace osu.Game.Tournament.Screens.Gameplay
Loop = true,
RelativeSizeAxes = Axes.Both,
},
- header = new MatchHeader(),
+ header = new MatchHeader
+ {
+ ShowLogo = false
+ },
new Container
{
RelativeSizeAxes = Axes.X,
diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs
index 6f62b3ddba..534c402f6c 100644
--- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs
+++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs
@@ -47,7 +47,7 @@ namespace osu.Game.Tournament.Screens.Ladder
RelativeSizeAxes = Axes.Both,
Loop = true,
},
- new DrawableTournamentTitleText
+ new DrawableTournamentHeaderText
{
Y = 100,
Anchor = Anchor.TopCentre,
diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs
index 4f3f7cfdbf..2b0bfe0b74 100644
--- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs
+++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Tournament.Screens.MapPool
new MatchHeader(),
mapFlows = new FillFlowContainer>
{
- Y = 100,
+ Y = 140,
Spacing = new Vector2(10, 10),
Padding = new MarginPadding(25),
Direction = FillDirection.Vertical,
@@ -235,6 +235,7 @@ namespace osu.Game.Tournament.Screens.MapPool
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
+ Height = 42,
});
}
}
diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs
index 0fcec645e3..88289ad6bd 100644
--- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs
+++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs
@@ -62,7 +62,7 @@ namespace osu.Game.Tournament.Screens.Schedule
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- new DrawableTournamentTitleText(),
+ new DrawableTournamentHeaderText(),
new Container
{
Margin = new MarginPadding { Top = 40 },
diff --git a/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs
index 513d84b594..d48e396b89 100644
--- a/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs
+++ b/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs
@@ -15,7 +15,6 @@ 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
{
@@ -140,9 +139,9 @@ namespace osu.Game.Tournament.Screens.TeamIntro
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 TournamentSpriteText { Text = beatmap.BeatmapInfo.Metadata.Title, Colour = TournamentGame.TEXT_COLOUR, },
+ new TournamentSpriteText { Text = "by", Colour = TournamentGame.TEXT_COLOUR, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
+ new TournamentSpriteText { Text = beatmap.BeatmapInfo.Metadata.Artist, Colour = TournamentGame.TEXT_COLOUR, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
}
},
new FillFlowContainer
@@ -154,8 +153,8 @@ namespace osu.Game.Tournament.Screens.TeamIntro
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) },
+ new TournamentSpriteText { Text = beatmap.Score.ToString("#,0"), Colour = TournamentGame.TEXT_COLOUR, Width = 80 },
+ new TournamentSpriteText { Text = "#" + beatmap.Seed.Value.ToString("#,0"), Colour = TournamentGame.TEXT_COLOUR, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
}
},
};
@@ -204,7 +203,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
new Box
{
RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
+ Colour = TournamentGame.TEXT_COLOUR,
},
new TournamentSpriteText
{
@@ -260,20 +259,18 @@ namespace osu.Game.Tournament.Screens.TeamIntro
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),
+ Colour = TournamentGame.TEXT_COLOUR,
+ Font = OsuFont.Torus.With(size: 22, weight: FontWeight.SemiBold),
},
new TournamentSpriteText
{
Text = right,
- Colour = colour,
+ Colour = TournamentGame.TEXT_COLOUR,
Anchor = Anchor.TopRight,
Origin = Anchor.TopLeft,
Font = OsuFont.Torus.With(size: 22, weight: FontWeight.Regular),
@@ -305,7 +302,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
{
Text = team?.FullName.Value ?? "???",
Font = OsuFont.Torus.With(size: 32, weight: FontWeight.SemiBold),
- Colour = Color4.Black,
+ Colour = TournamentGame.TEXT_COLOUR,
},
}
};
diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs
index 41165ca141..41822ae2c3 100644
--- a/osu.Game.Tournament/TournamentGameBase.cs
+++ b/osu.Game.Tournament/TournamentGameBase.cs
@@ -37,6 +37,8 @@ namespace osu.Game.Tournament
private Storage storage;
+ private TournamentStorage tournamentStorage;
+
private DependencyContainer dependencies;
private Bindable windowSize;
@@ -54,7 +56,9 @@ namespace osu.Game.Tournament
{
Resources.AddStore(new DllResourceStore(typeof(TournamentGameBase).Assembly));
- Textures.AddStore(new TextureLoaderStore(new ResourceStore(new StorageBackedResourceStore(storage))));
+ dependencies.CacheAs(tournamentStorage = new TournamentStorage(storage));
+
+ Textures.AddStore(new TextureLoaderStore(tournamentStorage));
this.storage = storage;
diff --git a/osu.Game.Tournament/TournamentStorage.cs b/osu.Game.Tournament/TournamentStorage.cs
new file mode 100644
index 0000000000..139ad3857b
--- /dev/null
+++ b/osu.Game.Tournament/TournamentStorage.cs
@@ -0,0 +1,19 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.IO.Stores;
+using osu.Framework.Platform;
+
+namespace osu.Game.Tournament
+{
+ internal class TournamentStorage : NamespacedResourceStore
+ {
+ public TournamentStorage(Storage storage)
+ : base(new StorageBackedResourceStore(storage), "tournament")
+ {
+ AddExtension("m4v");
+ AddExtension("avi");
+ AddExtension("mp4");
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs
index dcadbf4cf5..532efeb4bd 100644
--- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs
+++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Overlays.Changelog
public Action ListingSelected;
- public UpdateStreamBadgeArea Streams;
+ public ChangelogUpdateStreamControl Streams;
private const string listing_string = "listing";
@@ -95,7 +95,7 @@ namespace osu.Game.Overlays.Changelog
Horizontal = 65,
Vertical = 20
},
- Child = Streams = new UpdateStreamBadgeArea()
+ Child = Streams = new ChangelogUpdateStreamControl()
}
}
};
diff --git a/osu.Game/Overlays/Changelog/ChangelogUpdateStreamControl.cs b/osu.Game/Overlays/Changelog/ChangelogUpdateStreamControl.cs
new file mode 100644
index 0000000000..509a6dabae
--- /dev/null
+++ b/osu.Game/Overlays/Changelog/ChangelogUpdateStreamControl.cs
@@ -0,0 +1,12 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Online.API.Requests.Responses;
+
+namespace osu.Game.Overlays.Changelog
+{
+ public class ChangelogUpdateStreamControl : OverlayStreamControl
+ {
+ protected override OverlayStreamItem CreateStreamItem(APIUpdateStream value) => new ChangelogUpdateStreamItem(value);
+ }
+}
diff --git a/osu.Game/Overlays/Changelog/ChangelogUpdateStreamItem.cs b/osu.Game/Overlays/Changelog/ChangelogUpdateStreamItem.cs
new file mode 100644
index 0000000000..f8e1ac0c84
--- /dev/null
+++ b/osu.Game/Overlays/Changelog/ChangelogUpdateStreamItem.cs
@@ -0,0 +1,28 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using Humanizer;
+using osu.Game.Graphics;
+using osu.Game.Online.API.Requests.Responses;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays.Changelog
+{
+ public class ChangelogUpdateStreamItem : OverlayStreamItem
+ {
+ public ChangelogUpdateStreamItem(APIUpdateStream stream)
+ : base(stream)
+ {
+ if (stream.IsFeatured)
+ Width *= 2;
+ }
+
+ protected override string MainText => Value.DisplayName;
+
+ protected override string AdditionalText => Value.LatestBuild.DisplayVersion;
+
+ protected override string InfoText => Value.LatestBuild.Users > 0 ? $"{"user".ToQuantity(Value.LatestBuild.Users, "N0")} online" : null;
+
+ protected override Color4 GetBarColour(OsuColour colours) => Value.Colour;
+ }
+}
diff --git a/osu.Game/Overlays/Home/Friends/FriendsBundle.cs b/osu.Game/Overlays/Home/Friends/FriendsBundle.cs
new file mode 100644
index 0000000000..75d00dfef8
--- /dev/null
+++ b/osu.Game/Overlays/Home/Friends/FriendsBundle.cs
@@ -0,0 +1,25 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Game.Overlays.Home.Friends
+{
+ public class FriendsBundle
+ {
+ public FriendsOnlineStatus Status { get; }
+
+ public int Count { get; }
+
+ public FriendsBundle(FriendsOnlineStatus status, int count)
+ {
+ Status = status;
+ Count = count;
+ }
+ }
+
+ public enum FriendsOnlineStatus
+ {
+ All,
+ Online,
+ Offline
+ }
+}
diff --git a/osu.Game/Overlays/Home/Friends/FriendsOnlineStatusControl.cs b/osu.Game/Overlays/Home/Friends/FriendsOnlineStatusControl.cs
new file mode 100644
index 0000000000..196f01ab4a
--- /dev/null
+++ b/osu.Game/Overlays/Home/Friends/FriendsOnlineStatusControl.cs
@@ -0,0 +1,26 @@
+// Copyright (c) ppy Pty Ltd . 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 osu.Game.Users;
+
+namespace osu.Game.Overlays.Home.Friends
+{
+ public class FriendsOnlineStatusControl : OverlayStreamControl
+ {
+ protected override OverlayStreamItem CreateStreamItem(FriendsBundle value) => new FriendsOnlineStatusItem(value);
+
+ public void Populate(List users)
+ {
+ var userCount = users.Count;
+ var onlineUsersCount = users.Count(user => user.IsOnline);
+
+ AddItem(new FriendsBundle(FriendsOnlineStatus.All, userCount));
+ AddItem(new FriendsBundle(FriendsOnlineStatus.Online, onlineUsersCount));
+ AddItem(new FriendsBundle(FriendsOnlineStatus.Offline, userCount - onlineUsersCount));
+
+ Current.Value = Items.FirstOrDefault();
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Home/Friends/FriendsOnlineStatusItem.cs b/osu.Game/Overlays/Home/Friends/FriendsOnlineStatusItem.cs
new file mode 100644
index 0000000000..d9b780ce46
--- /dev/null
+++ b/osu.Game/Overlays/Home/Friends/FriendsOnlineStatusItem.cs
@@ -0,0 +1,39 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Game.Graphics;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays.Home.Friends
+{
+ public class FriendsOnlineStatusItem : OverlayStreamItem
+ {
+ public FriendsOnlineStatusItem(FriendsBundle value)
+ : base(value)
+ {
+ }
+
+ protected override string MainText => Value.Status.ToString();
+
+ protected override string AdditionalText => Value.Count.ToString();
+
+ protected override Color4 GetBarColour(OsuColour colours)
+ {
+ switch (Value.Status)
+ {
+ case FriendsOnlineStatus.All:
+ return Color4.White;
+
+ case FriendsOnlineStatus.Online:
+ return colours.GreenLight;
+
+ case FriendsOnlineStatus.Offline:
+ return Color4.Black;
+
+ default:
+ throw new ArgumentException($@"{Value.Status} status does not provide a colour in {nameof(GetBarColour)}.");
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs b/osu.Game/Overlays/OverlayStreamControl.cs
similarity index 62%
rename from osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs
rename to osu.Game/Overlays/OverlayStreamControl.cs
index ffb622dd37..8b6aca6d5d 100644
--- a/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs
+++ b/osu.Game/Overlays/OverlayStreamControl.cs
@@ -3,42 +3,32 @@
using osu.Framework.Graphics;
using osu.Framework.Input.Events;
-using osu.Game.Online.API.Requests.Responses;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics.UserInterface;
+using JetBrains.Annotations;
-namespace osu.Game.Overlays.Changelog
+namespace osu.Game.Overlays
{
- public class UpdateStreamBadgeArea : TabControl
+ public abstract class OverlayStreamControl : TabControl
{
- public UpdateStreamBadgeArea()
+ protected OverlayStreamControl()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
}
- public void Populate(List streams)
+ public void Populate(List streams) => streams.ForEach(AddItem);
+
+ protected override Dropdown CreateDropdown() => null;
+
+ protected override TabItem CreateTabItem(T value) => CreateStreamItem(value).With(item =>
{
- foreach (var updateStream in streams)
- AddItem(updateStream);
- }
+ item.SelectedItem.BindTo(Current);
+ });
- protected override bool OnHover(HoverEvent e)
- {
- foreach (var streamBadge in TabContainer.Children.OfType())
- streamBadge.UserHoveringArea = true;
-
- return base.OnHover(e);
- }
-
- protected override void OnHoverLost(HoverLostEvent e)
- {
- foreach (var streamBadge in TabContainer.Children.OfType())
- streamBadge.UserHoveringArea = false;
-
- base.OnHoverLost(e);
- }
+ [NotNull]
+ protected abstract OverlayStreamItem CreateStreamItem(T value);
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
{
@@ -47,9 +37,20 @@ namespace osu.Game.Overlays.Changelog
AllowMultiline = true,
};
- protected override Dropdown CreateDropdown() => null;
+ protected override bool OnHover(HoverEvent e)
+ {
+ foreach (var streamBadge in TabContainer.Children.OfType>())
+ streamBadge.UserHoveringArea = true;
- protected override TabItem CreateTabItem(APIUpdateStream value) =>
- new UpdateStreamBadge(value) { SelectedTab = { BindTarget = Current } };
+ return base.OnHover(e);
+ }
+
+ protected override void OnHoverLost(HoverLostEvent e)
+ {
+ foreach (var streamBadge in TabContainer.Children.OfType>())
+ streamBadge.UserHoveringArea = false;
+
+ base.OnHoverLost(e);
+ }
}
}
diff --git a/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs b/osu.Game/Overlays/OverlayStreamItem.cs
similarity index 74%
rename from osu.Game/Overlays/Changelog/UpdateStreamBadge.cs
rename to osu.Game/Overlays/OverlayStreamItem.cs
index 6786bbc49f..630d3a0a22 100644
--- a/osu.Game/Overlays/Changelog/UpdateStreamBadge.cs
+++ b/osu.Game/Overlays/OverlayStreamItem.cs
@@ -1,46 +1,52 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using Humanizer;
-using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
-using osu.Game.Graphics;
-using osu.Game.Online.API.Requests.Responses;
using osu.Framework.Graphics.UserInterface;
-using osu.Game.Graphics.Sprites;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
-using osuTK;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Allocation;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics;
+using osuTK.Graphics;
-namespace osu.Game.Overlays.Changelog
+namespace osu.Game.Overlays
{
- public class UpdateStreamBadge : TabItem
+ public abstract class OverlayStreamItem : TabItem
{
- private const float badge_width = 100;
- private const float transition_duration = 100;
+ public readonly Bindable SelectedItem = new Bindable();
- public readonly Bindable SelectedTab = new Bindable();
+ private bool userHoveringArea;
- private readonly APIUpdateStream stream;
+ public bool UserHoveringArea
+ {
+ set
+ {
+ if (value == userHoveringArea)
+ return;
+
+ userHoveringArea = value;
+ updateState();
+ }
+ }
private FillFlowContainer text;
private ExpandingBar expandingBar;
- public UpdateStreamBadge(APIUpdateStream stream)
- : base(stream)
+ protected OverlayStreamItem(T value)
+ : base(value)
{
- this.stream = stream;
+ Height = 60;
+ Width = 100;
+ Padding = new MarginPadding(5);
}
[BackgroundDependencyLoader]
- private void load(OverlayColourProvider colourProvider)
+ private void load(OverlayColourProvider colourProvider, OsuColour colours)
{
- Size = new Vector2(stream.IsFeatured ? badge_width * 2 : badge_width, 60);
- Padding = new MarginPadding(5);
-
AddRange(new Drawable[]
{
text = new FillFlowContainer
@@ -52,17 +58,17 @@ namespace osu.Game.Overlays.Changelog
{
new OsuSpriteText
{
- Text = stream.DisplayName,
+ Text = MainText,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Black),
},
new OsuSpriteText
{
- Text = stream.LatestBuild.DisplayVersion,
+ Text = AdditionalText,
Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular),
},
new OsuSpriteText
{
- Text = stream.LatestBuild.Users > 0 ? $"{"user".ToQuantity(stream.LatestBuild.Users, "N0")} online" : null,
+ Text = InfoText,
Font = OsuFont.GetFont(size: 10),
Colour = colourProvider.Foreground1
},
@@ -71,7 +77,7 @@ namespace osu.Game.Overlays.Changelog
expandingBar = new ExpandingBar
{
Anchor = Anchor.TopCentre,
- Colour = stream.Colour,
+ Colour = GetBarColour(colours),
ExpandedSize = 4,
CollapsedSize = 2,
Expanded = true
@@ -79,9 +85,17 @@ namespace osu.Game.Overlays.Changelog
new HoverClickSounds()
});
- SelectedTab.BindValueChanged(_ => updateState(), true);
+ SelectedItem.BindValueChanged(_ => updateState(), true);
}
+ protected abstract string MainText { get; }
+
+ protected abstract string AdditionalText { get; }
+
+ protected virtual string InfoText => string.Empty;
+
+ protected abstract Color4 GetBarColour(OsuColour colours);
+
protected override void OnActivated() => updateState();
protected override void OnDeactivated() => updateState();
@@ -104,7 +118,7 @@ namespace osu.Game.Overlays.Changelog
bool textHighlighted = IsHovered;
bool barExpanded = IsHovered;
- if (SelectedTab.Value == null)
+ if (SelectedItem.Value == null)
{
// at listing, all badges are highlighted when user is not hovering any badge.
textHighlighted |= !userHoveringArea;
@@ -120,21 +134,7 @@ namespace osu.Game.Overlays.Changelog
}
expandingBar.Expanded = barExpanded;
- text.FadeTo(textHighlighted ? 1 : 0.5f, transition_duration, Easing.OutQuint);
- }
-
- private bool userHoveringArea;
-
- public bool UserHoveringArea
- {
- set
- {
- if (value == userHoveringArea)
- return;
-
- userHoveringArea = value;
- updateState();
- }
+ text.FadeTo(textHighlighted ? 1 : 0.5f, 100, Easing.OutQuint);
}
}
}
diff --git a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs
index 4bff8146b4..3478f18a40 100644
--- a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs
+++ b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs
@@ -14,22 +14,8 @@ namespace osu.Game.Overlays.Volume
public Func ActionRequested;
public Func ScrollActionRequested;
- 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 OnPressed(GlobalAction action) =>
+ ActionRequested?.Invoke(action) ?? false;
public bool OnScroll(GlobalAction action, float amount, bool isPrecise) =>
ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false;
diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs
index 4e51ff939a..be5762e68d 100644
--- a/osu.Game/Screens/Menu/IntroTriangles.cs
+++ b/osu.Game/Screens/Menu/IntroTriangles.cs
@@ -259,11 +259,18 @@ namespace osu.Game.Screens.Menu
private class LazerLogo : CompositeDrawable
{
+ private readonly Stream videoStream;
+
public LazerLogo(Stream videoStream)
{
+ this.videoStream = videoStream;
Size = new Vector2(960);
+ }
- InternalChild = new VideoSprite(videoStream)
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ InternalChild = new VideoSprite(videoStream, false)
{
RelativeSizeAxes = Axes.Both,
Clock = new FramedOffsetClock(Clock) { Offset = -logo_1 }
diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs
index 71744d8b80..04c08cdbd2 100644
--- a/osu.Game/Screens/Select/BeatmapCarousel.cs
+++ b/osu.Game/Screens/Select/BeatmapCarousel.cs
@@ -153,9 +153,11 @@ namespace osu.Game.Screens.Select
beatmaps.BeatmapHidden += beatmapHidden;
beatmaps.BeatmapRestored += beatmapRestored;
- loadBeatmapSets(beatmaps.GetAllUsableBeatmapSetsEnumerable());
+ loadBeatmapSets(GetLoadableBeatmaps());
}
+ protected virtual IEnumerable GetLoadableBeatmaps() => beatmaps.GetAllUsableBeatmapSetsEnumerable();
+
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() =>
{
var existingSet = beatmapSets.FirstOrDefault(b => b.BeatmapSet.ID == beatmapSet.ID);
diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs
index fa4de21eec..52328d43b2 100644
--- a/osu.Game/Skinning/LegacySkinExtensions.cs
+++ b/osu.Game/Skinning/LegacySkinExtensions.cs
@@ -61,7 +61,7 @@ namespace osu.Game.Skinning
{
var iniRate = source.GetConfig(GlobalSkinConfiguration.AnimationFramerate);
- if (iniRate != null)
+ if (iniRate?.Value > 0)
return 1000f / iniRate.Value;
return 1000f / textures.Length;
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index cc1ab654ab..ba6f0e2251 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -23,7 +23,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 04b688cfa3..54cd400d51 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -71,7 +71,7 @@
-
+
@@ -79,7 +79,7 @@
-
+