diff --git a/.idea/.idea.osu.Desktop/.idea/modules.xml b/.idea/.idea.osu.Desktop/.idea/modules.xml
index fe63f5faf3..366f172c30 100644
--- a/.idea/.idea.osu.Desktop/.idea/modules.xml
+++ b/.idea/.idea.osu.Desktop/.idea/modules.xml
@@ -2,6 +2,7 @@
+
diff --git a/README.md b/README.md
index 77c7eb9d2d..59d72247f5 100644
--- a/README.md
+++ b/README.md
@@ -93,7 +93,7 @@ JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it
We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention of having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time to ensure no effort is wasted.
-If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues) (especially those with the ["good first issue"](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) label).
+If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues) (especially those with the ["good first issue"](https://github.com/ppy/osu/issues?q=is%3Aopen+label%3Agood-first-issue+sort%3Aupdated-desc) label).
Before starting, please make sure you are familiar with the [development and testing](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) procedure we have set up. New component development, and where possible, bug fixing and debugging existing components **should always be done under VisualTests**.
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index 510b53054b..4fd0e5e8c7 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -97,8 +97,10 @@ platform :ios do
changelog.gsub!('$BUILD_ID', options[:build])
pilot(
- wait_processing_interval: 1800,
+ wait_processing_interval: 900,
changelog: changelog,
+ groups: ['osu! supporters', 'public'],
+ distribute_external: true,
ipa: './osu.iOS/bin/iPhone/Release/osu.iOS.ipa'
)
end
diff --git a/osu.Android.props b/osu.Android.props
index 1a63b893a1..7e17f9da16 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,6 +52,6 @@
-
+
diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs
index b9d791fdb1..212365caad 100644
--- a/osu.Game.Rulesets.Catch/CatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs
@@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Catch
new KeyBinding(InputKey.Shift, CatchAction.Dash),
};
- public override IEnumerable ConvertLegacyMods(LegacyMods mods)
+ public override IEnumerable ConvertFromLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
yield return new CatchModNightcore();
diff --git a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs
index b41a5e0612..9dab3ed630 100644
--- a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs
+++ b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs
@@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Catch.Replays
}
}
- public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
+ public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
Position = currentFrame.Position.X / CatchPlayfield.BASE_WIDTH;
Dashing = currentFrame.ButtonState == ReplayButtonState.Left1;
@@ -56,5 +56,14 @@ namespace osu.Game.Rulesets.Catch.Replays
Actions.Add(CatchAction.MoveLeft);
}
}
+
+ public LegacyReplayFrame ToLegacy(IBeatmap beatmap)
+ {
+ ReplayButtonState state = ReplayButtonState.None;
+
+ if (Actions.Contains(CatchAction.Dash)) state |= ReplayButtonState.Left1;
+
+ return new LegacyReplayFrame(Time, Position * CatchPlayfield.BASE_WIDTH, null, state);
+ }
}
}
diff --git a/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs b/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs
new file mode 100644
index 0000000000..9a4d1f9585
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.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 osu.Game.Replays;
+using osu.Game.Rulesets.Catch.Replays;
+using osu.Game.Rulesets.Replays;
+using osu.Game.Rulesets.UI;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.UI
+{
+ public class CatchReplayRecorder : ReplayRecorder
+ {
+ private readonly CatchPlayfield playfield;
+
+ public CatchReplayRecorder(Replay target, CatchPlayfield playfield)
+ : base(target)
+ {
+ this.playfield = playfield;
+ }
+
+ protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame)
+ => new CatchReplayFrame(Time.Current, playfield.CatcherArea.MovableCatcher.X, actions.Contains(CatchAction.Dash), previousFrame as CatchReplayFrame);
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs
index a3dc58bc19..e361b29a9d 100644
--- a/osu.Game.Rulesets.Catch/UI/Catcher.cs
+++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs
@@ -7,6 +7,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
@@ -55,14 +56,14 @@ namespace osu.Game.Rulesets.Catch.UI
}
///
- /// Activate or deactive the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met.
+ /// Activate or deactivate the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met.
///
protected bool Trail
{
get => trail;
set
{
- if (value == trail) return;
+ if (value == trail || AdditiveTarget == null) return;
trail = value;
@@ -77,6 +78,8 @@ namespace osu.Game.Rulesets.Catch.UI
private CatcherSprite catcherKiai;
private CatcherSprite catcherFail;
+ private CatcherSprite currentCatcher;
+
private int currentDirection;
private bool dashing;
@@ -236,10 +239,10 @@ namespace osu.Game.Rulesets.Catch.UI
this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint);
Trail = true;
- var hyperDashEndGlow = createAdditiveSprite(true);
+ var hyperDashEndGlow = createAdditiveSprite();
- hyperDashEndGlow.MoveToOffset(new Vector2(0, -20), 1200, Easing.In);
- hyperDashEndGlow.ScaleTo(hyperDashEndGlow.Scale * 0.9f).ScaleTo(hyperDashEndGlow.Scale * 1.2f, 1200, Easing.In);
+ hyperDashEndGlow.MoveToOffset(new Vector2(0, -10), 1200, Easing.In);
+ hyperDashEndGlow.ScaleTo(hyperDashEndGlow.Scale * 0.95f).ScaleTo(hyperDashEndGlow.Scale * 1.2f, 1200, Easing.In);
hyperDashEndGlow.FadeOut(1200);
hyperDashEndGlow.Expire(true);
}
@@ -358,39 +361,36 @@ namespace osu.Game.Rulesets.Catch.UI
private void updateCatcher()
{
- catcherIdle.Hide();
- catcherKiai.Hide();
- catcherFail.Hide();
-
- CatcherSprite current;
+ currentCatcher?.Hide();
switch (CurrentState)
{
default:
- current = catcherIdle;
+ currentCatcher = catcherIdle;
break;
case CatcherAnimationState.Fail:
- current = catcherFail;
+ currentCatcher = catcherFail;
break;
case CatcherAnimationState.Kiai:
- current = catcherKiai;
+ currentCatcher = catcherKiai;
break;
}
- current.Show();
- (current.Drawable as IAnimation)?.GotoFrame(0);
+ currentCatcher.Show();
+ (currentCatcher.Drawable as IAnimation)?.GotoFrame(0);
}
private void beginTrail()
{
- Trail &= dashing || HyperDashing;
- Trail &= AdditiveTarget != null;
+ if (!dashing && !HyperDashing)
+ {
+ Trail = false;
+ return;
+ }
- if (!Trail) return;
-
- var additive = createAdditiveSprite(HyperDashing);
+ var additive = createAdditiveSprite();
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint);
additive.Expire(true);
@@ -398,27 +398,6 @@ namespace osu.Game.Rulesets.Catch.UI
Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
}
- private Drawable createAdditiveSprite(bool hyperDash)
- {
- var additive = createCatcherSprite();
-
- additive.Anchor = Anchor;
- additive.Scale = Scale;
- additive.Colour = hyperDash ? Color4.Red : Color4.White;
- additive.Blending = BlendingParameters.Additive;
- additive.RelativePositionAxes = RelativePositionAxes;
- additive.Position = Position;
-
- AdditiveTarget.Add(additive);
-
- return additive;
- }
-
- private Drawable createCatcherSprite()
- {
- return new CatcherSprite(CurrentState);
- }
-
private void updateState(CatcherAnimationState state)
{
if (CurrentState == state)
@@ -428,6 +407,25 @@ namespace osu.Game.Rulesets.Catch.UI
updateCatcher();
}
+ private CatcherTrailSprite createAdditiveSprite()
+ {
+ var tex = (currentCatcher.Drawable as TextureAnimation)?.CurrentFrame ?? ((Sprite)currentCatcher.Drawable).Texture;
+
+ var sprite = new CatcherTrailSprite(tex)
+ {
+ Anchor = Anchor,
+ Scale = Scale,
+ Colour = HyperDashing ? Color4.Red : Color4.White,
+ Blending = BlendingParameters.Additive,
+ RelativePositionAxes = RelativePositionAxes,
+ Position = Position
+ };
+
+ AdditiveTarget?.Add(sprite);
+
+ return sprite;
+ }
+
private void removeFromPlateWithTransform(DrawableHitObject fruit, Action action)
{
if (ExplodingFruitTarget != null)
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs
new file mode 100644
index 0000000000..56cb7dbfda
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs
@@ -0,0 +1,22 @@
+// 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.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.UI
+{
+ public class CatcherTrailSprite : Sprite
+ {
+ public CatcherTrailSprite(Texture texture)
+ {
+ Texture = texture;
+
+ Size = new Vector2(CatcherArea.CATCHER_SIZE);
+
+ // Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling.
+ OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
index 50c4154c61..4df2bc0f52 100644
--- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
@@ -40,6 +40,8 @@ namespace osu.Game.Rulesets.Catch.UI
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
+ protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new CatchReplayRecorder(replay, (CatchPlayfield)Playfield);
+
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, CreateDrawableRepresentation);
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new CatchPlayfieldAdjustmentContainer();
diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaInputTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaInputTestScene.cs
index 909d0d45c6..9049bb3a82 100644
--- a/osu.Game.Rulesets.Mania.Tests/ManiaInputTestScene.cs
+++ b/osu.Game.Rulesets.Mania.Tests/ManiaInputTestScene.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Tests
{
}
- protected override RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ protected override KeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
=> new LocalKeyBindingContainer(ruleset, variant, unique);
private class LocalKeyBindingContainer : RulesetKeyBindingContainer
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index b7b523a94d..9d06bd7c25 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania
public override ISkin CreateLegacySkinProvider(ISkinSource source) => new ManiaLegacySkinTransformer(source);
- public override IEnumerable ConvertLegacyMods(LegacyMods mods)
+ public override IEnumerable ConvertFromLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
yield return new ManiaModNightcore();
@@ -118,6 +118,59 @@ namespace osu.Game.Rulesets.Mania
yield return new ManiaModRandom();
}
+ public override LegacyMods ConvertToLegacyMods(Mod[] mods)
+ {
+ var value = base.ConvertToLegacyMods(mods);
+
+ foreach (var mod in mods)
+ {
+ switch (mod)
+ {
+ case ManiaModKey1 _:
+ value |= LegacyMods.Key1;
+ break;
+
+ case ManiaModKey2 _:
+ value |= LegacyMods.Key2;
+ break;
+
+ case ManiaModKey3 _:
+ value |= LegacyMods.Key3;
+ break;
+
+ case ManiaModKey4 _:
+ value |= LegacyMods.Key4;
+ break;
+
+ case ManiaModKey5 _:
+ value |= LegacyMods.Key5;
+ break;
+
+ case ManiaModKey6 _:
+ value |= LegacyMods.Key6;
+ break;
+
+ case ManiaModKey7 _:
+ value |= LegacyMods.Key7;
+ break;
+
+ case ManiaModKey8 _:
+ value |= LegacyMods.Key8;
+ break;
+
+ case ManiaModKey9 _:
+ value |= LegacyMods.Key9;
+ break;
+
+ case ManiaModFadeIn _:
+ value |= LegacyMods.FadeIn;
+ break;
+ }
+ }
+
+ return value;
+ }
+
public override IEnumerable GetModsFor(ModType type)
{
switch (type)
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs
index 14b36fb765..699c58c373 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs
@@ -3,24 +3,17 @@
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
-using osu.Framework.Graphics.Sprites;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
-using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
- public class ManiaModRandom : Mod, IApplicableToBeatmap
+ public class ManiaModRandom : ModRandom, IApplicableToBeatmap
{
- public override string Name => "Random";
- public override string Acronym => "RD";
- public override ModType Type => ModType.Conversion;
- public override IconUsage? Icon => OsuIcon.Dice;
public override string Description => @"Shuffle around the keys!";
- public override double ScoreMultiplier => 1;
public void ApplyToBeatmap(IBeatmap beatmap)
{
diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs
index 877a9ee410..b93e372027 100644
--- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs
+++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Mania.Replays
Actions.AddRange(actions);
}
- public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
+ public void FromLegacy(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
// We don't need to fully convert, just create the converter
var converter = new ManiaBeatmapConverter(beatmap, new ManiaRuleset());
@@ -56,5 +56,42 @@ namespace osu.Game.Rulesets.Mania.Replays
activeColumns >>= 1;
}
}
+
+ public LegacyReplayFrame ToLegacy(IBeatmap beatmap)
+ {
+ int keys = 0;
+
+ var converter = new ManiaBeatmapConverter(beatmap, new ManiaRuleset());
+
+ var stage = new StageDefinition { Columns = converter.TargetColumns };
+
+ var specialColumns = new List();
+
+ for (int i = 0; i < converter.TargetColumns; i++)
+ {
+ if (stage.IsSpecialColumn(i))
+ specialColumns.Add(i);
+ }
+
+ foreach (var action in Actions)
+ {
+ switch (action)
+ {
+ case ManiaAction.Special1:
+ keys |= 1 << specialColumns[0];
+ break;
+
+ case ManiaAction.Special2:
+ keys |= 1 << specialColumns[1];
+ break;
+
+ default:
+ keys |= 1 << (action - ManiaAction.Key1);
+ break;
+ }
+ }
+
+ return new LegacyReplayFrame(Time, keys, null, ReplayButtonState.None);
+ }
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
index 8e56144752..3784779de1 100644
--- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
@@ -88,5 +88,7 @@ namespace osu.Game.Rulesets.Mania.UI
}
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
+
+ protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new ManiaReplayRecorder(replay);
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaReplayRecorder.cs b/osu.Game.Rulesets.Mania/UI/ManiaReplayRecorder.cs
new file mode 100644
index 0000000000..18275000a2
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/UI/ManiaReplayRecorder.cs
@@ -0,0 +1,23 @@
+// 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 osu.Game.Replays;
+using osu.Game.Rulesets.Mania.Replays;
+using osu.Game.Rulesets.Replays;
+using osu.Game.Rulesets.UI;
+using osuTK;
+
+namespace osu.Game.Rulesets.Mania.UI
+{
+ public class ManiaReplayRecorder : ReplayRecorder
+ {
+ public ManiaReplayRecorder(Replay replay)
+ : base(replay)
+ {
+ }
+
+ protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame)
+ => new ManiaReplayFrame(Time.Current, actions.ToArray());
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs
index 67b6dac787..0649989dc0 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs
@@ -4,48 +4,43 @@
using System;
using NUnit.Framework;
using osu.Framework.Graphics;
-using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
+using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests
{
- public class TestSceneHitCircleArea : ManualInputManagerTestScene
+ public class TestSceneHitCircleArea : OsuManualInputManagerTestScene
{
private HitCircle hitCircle;
private DrawableHitCircle drawableHitCircle;
private DrawableHitCircle.HitReceptor hitAreaReceptor => drawableHitCircle.HitArea;
[SetUp]
- public new void SetUp()
+ public void SetUp() => Schedule(() =>
{
- base.SetUp();
-
- Schedule(() =>
+ hitCircle = new HitCircle
{
- hitCircle = new HitCircle
- {
- Position = new Vector2(100, 100),
- StartTime = Time.Current + 500
- };
+ Position = new Vector2(100, 100),
+ StartTime = Time.Current + 500
+ };
- hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+ hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
- Child = new SkinProvidingContainer(new DefaultSkin())
+ Child = new SkinProvidingContainer(new DefaultSkin())
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = drawableHitCircle = new DrawableHitCircle(hitCircle)
{
- RelativeSizeAxes = Axes.Both,
- Child = drawableHitCircle = new DrawableHitCircle(hitCircle)
- {
- Size = new Vector2(100)
- }
- };
- });
- }
+ Size = new Vector2(100)
+ }
+ };
+ });
[Test]
public void TestCircleHitCentre()
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs
index 4af4d5f966..0ae49790cd 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs
@@ -23,7 +23,7 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Tests
{
- public class TestSceneOsuDistanceSnapGrid : ManualInputManagerTestScene
+ public class TestSceneOsuDistanceSnapGrid : OsuManualInputManagerTestScene
{
private const double beat_length = 100;
private static readonly Vector2 grid_position = new Vector2(512, 384);
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneResumeOverlay.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneResumeOverlay.cs
index 8e73d6152f..f4809b0c9b 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneResumeOverlay.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneResumeOverlay.cs
@@ -12,7 +12,7 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests
{
- public class TestSceneResumeOverlay : ManualInputManagerTestScene
+ public class TestSceneResumeOverlay : OsuManualInputManagerTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
index defd3a6f22..a201364de4 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
@@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Tests
typeof(DrawableSliderTick),
typeof(DrawableSliderTail),
typeof(DrawableSliderHead),
- typeof(DrawableRepeatPoint),
+ typeof(DrawableSliderRepeat),
typeof(DrawableOsuHitObject)
};
@@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddAssert("head samples updated", () => assertSamples(((Slider)slider.HitObject).HeadCircle));
AddAssert("tick samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertTickSamples));
- AddAssert("repeat samples updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertSamples));
+ AddAssert("repeat samples updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertSamples));
AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0);
static bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick";
@@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddAssert("head samples not updated", () => assertSamples(((Slider)slider.HitObject).HeadCircle));
AddAssert("tick samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertTickSamples));
- AddAssert("repeat samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertSamples));
+ AddAssert("repeat samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertSamples));
AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0);
static bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick";
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
index 94df239267..67e1b77770 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Tests
typeof(SliderBall),
typeof(DrawableSlider),
typeof(DrawableSliderTick),
- typeof(DrawableRepeatPoint),
+ typeof(DrawableSliderRepeat),
typeof(DrawableOsuHitObject),
typeof(DrawableSliderHead),
typeof(DrawableSliderTail),
@@ -327,7 +327,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddAssert("Tracking dropped", assertMidSliderJudgementFail);
}
- private bool assertGreatJudge() => judgementResults.Last().Type == HitResult.Great;
+ private bool assertGreatJudge() => judgementResults.Any() && judgementResults.All(t => t.Type == HitResult.Great);
private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss;
diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuIgnoreJudgement.cs b/osu.Game.Rulesets.Osu/Judgements/OsuIgnoreJudgement.cs
new file mode 100644
index 0000000000..e528f65dca
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Judgements/OsuIgnoreJudgement.cs
@@ -0,0 +1,16 @@
+// 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.Rulesets.Scoring;
+
+namespace osu.Game.Rulesets.Osu.Judgements
+{
+ public class OsuIgnoreJudgement : OsuJudgement
+ {
+ public override bool AffectsCombo => false;
+
+ protected override int NumericResultFor(HitResult result) => 0;
+
+ protected override double HealthIncreaseFor(HitResult result) => 0;
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs
index bc5f79331f..cf6677a55d 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs
@@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods
return;
slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
- slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
+ slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
foreach (var point in slider.Path.ControlPoints)
point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y);
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
index 41daef1f38..44dba7715a 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
@@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Mods
case DrawableSliderHead _:
case DrawableSliderTail _:
case DrawableSliderTick _:
- case DrawableRepeatPoint _:
+ case DrawableSliderRepeat _:
return;
default:
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
index cc2f4c3f70..297a0fea79 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
@@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Mods
// Wiggle the repeat points with the slider instead of independently.
// Also fixes an issue with repeat points being positioned incorrectly.
- if (osuObject is RepeatPoint)
+ if (osuObject is SliderRepeat)
return;
Random objRand = new Random((int)osuObject.StartTime);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
index 3e9c0f341b..d0935e46f7 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
@@ -88,8 +88,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
private void refresh()
{
- ClearInternal();
-
OsuHitObject osuStart = Start.HitObject;
double startTime = osuStart.GetEndTime();
@@ -116,6 +114,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
double? firstTransformStartTime = null;
double finalTransformEndTime = startTime;
+ int point = 0;
+
for (int d = (int)(spacing * 1.5); d < distance - spacing; d += spacing)
{
float fraction = (float)d / distance;
@@ -126,13 +126,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
FollowPoint fp;
- AddInternal(fp = new FollowPoint
+ if (InternalChildren.Count > point)
{
- Position = pointStartPosition,
- Rotation = rotation,
- Alpha = 0,
- Scale = new Vector2(1.5f * osuEnd.Scale),
- });
+ fp = (FollowPoint)InternalChildren[point];
+ fp.ClearTransforms();
+ }
+ else
+ AddInternal(fp = new FollowPoint());
+
+ fp.Position = pointStartPosition;
+ fp.Rotation = rotation;
+ fp.Alpha = 0;
+ fp.Scale = new Vector2(1.5f * osuEnd.Scale);
if (firstTransformStartTime == null)
firstTransformStartTime = fadeInTime;
@@ -146,8 +151,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
finalTransformEndTime = fadeOutTime + osuEnd.TimeFadeIn;
}
+
+ point++;
}
+ int excessPoints = InternalChildren.Count - point;
+ for (int i = 0; i < excessPoints; i++)
+ RemoveInternal(InternalChildren[^1]);
+
// todo: use Expire() on FollowPoints and take lifetime from them when https://github.com/ppy/osu-framework/issues/3300 is fixed.
LifetimeStart = firstTransformStartTime ?? startTime;
LifetimeEnd = finalTransformEndTime;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index ccc731779d..2d5b9d874c 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -6,13 +6,11 @@ using osuTK;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
-using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Skinning;
-using osu.Game.Rulesets.Scoring;
using osuTK.Graphics;
using osu.Game.Skinning;
@@ -26,12 +24,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public readonly SliderBall Ball;
public readonly SkinnableDrawable Body;
+ public override bool DisplayResult => false;
+
private PlaySliderBody sliderBody => Body.Drawable as PlaySliderBody;
private readonly Container headContainer;
private readonly Container tailContainer;
private readonly Container tickContainer;
- private readonly Container repeatContainer;
+ private readonly Container repeatContainer;
private readonly Slider slider;
@@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
Body = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBody), _ => new DefaultSliderBody(), confineMode: ConfineMode.NoScaling),
tickContainer = new Container { RelativeSizeAxes = Axes.Both },
- repeatContainer = new Container { RelativeSizeAxes = Axes.Both },
+ repeatContainer = new Container { RelativeSizeAxes = Axes.Both },
Ball = new SliderBall(s, this)
{
GetInitialHitAction = () => HeadCircle.HitAction,
@@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
tickContainer.Add(tick);
break;
- case DrawableRepeatPoint repeat:
+ case DrawableSliderRepeat repeat:
repeatContainer.Add(repeat);
break;
}
@@ -129,8 +129,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
case SliderTick tick:
return new DrawableSliderTick(tick) { Position = tick.Position - slider.Position };
- case RepeatPoint repeat:
- return new DrawableRepeatPoint(repeat, this) { Position = repeat.Position - slider.Position };
+ case SliderRepeat repeat:
+ return new DrawableSliderRepeat(repeat, this) { Position = repeat.Position - slider.Position };
}
return base.CreateNestedHitObject(hitObject);
@@ -185,7 +185,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.ApplySkin(skin, allowFallback);
bool allowBallTint = skin.GetConfig(OsuSkinConfiguration.AllowSliderBallTint)?.Value ?? false;
- Ball.AccentColour = allowBallTint ? AccentColour.Value : Color4.White;
+ Ball.Colour = allowBallTint ? AccentColour.Value : Color4.White;
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
@@ -193,22 +193,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (userTriggered || Time.Current < slider.EndTime)
return;
- ApplyResult(r =>
- {
- var judgementsCount = NestedHitObjects.Count;
- var judgementsHit = NestedHitObjects.Count(h => h.IsHit);
-
- var hitFraction = (double)judgementsHit / judgementsCount;
-
- if (hitFraction == 1 && HeadCircle.Result.Type == HitResult.Great)
- r.Type = HitResult.Great;
- else if (hitFraction >= 0.5 && HeadCircle.Result.Type >= HitResult.Good)
- r.Type = HitResult.Good;
- else if (hitFraction > 0)
- r.Type = HitResult.Meh;
- else
- r.Type = HitResult.Miss;
- });
+ ApplyResult(r => r.Type = r.Judgement.MaxResult);
}
protected override void UpdateStateTransforms(ArmedState state)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
similarity index 89%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
rename to osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
index 8fdcd060e7..b9cee71ca1 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
@@ -14,19 +14,19 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
- public class DrawableRepeatPoint : DrawableOsuHitObject, ITrackSnaking
+ public class DrawableSliderRepeat : DrawableOsuHitObject, ITrackSnaking
{
- private readonly RepeatPoint repeatPoint;
+ private readonly SliderRepeat sliderRepeat;
private readonly DrawableSlider drawableSlider;
private double animDuration;
private readonly Drawable scaleContainer;
- public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider)
- : base(repeatPoint)
+ public DrawableSliderRepeat(SliderRepeat sliderRepeat, DrawableSlider drawableSlider)
+ : base(sliderRepeat)
{
- this.repeatPoint = repeatPoint;
+ this.sliderRepeat = sliderRepeat;
this.drawableSlider = drawableSlider;
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
@@ -48,13 +48,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
- if (repeatPoint.StartTime <= Time.Current)
+ if (sliderRepeat.StartTime <= Time.Current)
ApplyResult(r => r.Type = drawableSlider.Tracking.Value ? HitResult.Great : HitResult.Miss);
}
protected override void UpdateInitialTransforms()
{
- animDuration = Math.Min(300, repeatPoint.SpanDuration);
+ animDuration = Math.Min(300, sliderRepeat.SpanDuration);
this.Animate(
d => d.FadeIn(animDuration),
@@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public void UpdateSnakingPosition(Vector2 start, Vector2 end)
{
- bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0;
+ bool isRepeatAtEnd = sliderRepeat.RepeatIndex % 2 == 0;
List curve = ((PlaySliderBody)drawableSlider.Body.Drawable).CurrentCurve;
Position = isRepeatAtEnd ? end : start;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
index 21a3a0d236..29a4929c1b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
@@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered && timeOffset >= 0)
- ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
+ ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : HitResult.Miss);
}
private void updatePosition() => Position = HitObject.Position - slider.Position;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
index 60b5c335d6..66eb60aa28 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
@@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (timeOffset >= 0)
- ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
+ ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : HitResult.Miss);
}
protected override void UpdateInitialTransforms()
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
index e3dd2b1b4f..3de30d51d9 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
@@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
.FadeTo(tracking_alpha, 250, Easing.OutQuint);
}
- this.RotateTo(currentRotation / 2, validAndTracking ? 500 : 1500, Easing.OutExpo);
+ Rotation = (float)Interpolation.Lerp(Rotation, currentRotation / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1));
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 77f8ec6cc8..db1f46d8e2 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -177,7 +177,7 @@ namespace osu.Game.Rulesets.Osu.Objects
break;
case SliderEventType.Repeat:
- AddNested(new RepeatPoint
+ AddNested(new SliderRepeat
{
RepeatIndex = e.SpanIndex,
SpanDuration = SpanDuration,
@@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Osu.Objects
foreach (var tick in NestedHitObjects.OfType())
tick.Samples = sampleList;
- foreach (var repeat in NestedHitObjects.OfType())
+ foreach (var repeat in NestedHitObjects.OfType())
repeat.Samples = getNodeSamples(repeat.RepeatIndex + 1);
if (HeadCircle != null)
@@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Osu.Objects
private IList getNodeSamples(int nodeIndex) =>
nodeIndex < NodeSamples.Count ? NodeSamples[nodeIndex] : Samples;
- public override Judgement CreateJudgement() => new OsuJudgement();
+ public override Judgement CreateJudgement() => new OsuIgnoreJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
diff --git a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/SliderRepeat.cs
similarity index 77%
rename from osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
rename to osu.Game.Rulesets.Osu/Objects/SliderRepeat.cs
index a277517f9f..a8fd3764c5 100644
--- a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderRepeat.cs
@@ -10,7 +10,7 @@ using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects
{
- public class RepeatPoint : OsuHitObject
+ public class SliderRepeat : OsuHitObject
{
public int RepeatIndex { get; set; }
public double SpanDuration { get; set; }
@@ -28,8 +28,15 @@ namespace osu.Game.Rulesets.Osu.Objects
TimePreempt = Math.Min(SpanDuration * 2, TimePreempt);
}
- public override Judgement CreateJudgement() => new OsuJudgement();
-
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
+
+ public override Judgement CreateJudgement() => new SliderRepeatJudgement();
+
+ public class SliderRepeatJudgement : OsuJudgement
+ {
+ public override bool IsBonus => true;
+
+ protected override int NumericResultFor(HitResult result) => result == MaxResult ? 30 : 0;
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
index 127c36fcc0..c11e20c9e7 100644
--- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
@@ -22,8 +22,8 @@ namespace osu.Game.Rulesets.Osu.Objects
pathVersion.BindValueChanged(_ => Position = slider.EndPosition);
}
- public override Judgement CreateJudgement() => new IgnoreJudgement();
-
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
+
+ public override Judgement CreateJudgement() => new SliderRepeat.SliderRepeatJudgement();
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
index a49f4cef8b..212a84c04a 100644
--- a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
@@ -30,8 +30,15 @@ namespace osu.Game.Rulesets.Osu.Objects
TimePreempt = (StartTime - SpanStartTime) / 2 + offset;
}
- public override Judgement CreateJudgement() => new OsuJudgement();
-
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
+
+ public override Judgement CreateJudgement() => new SliderTickJudgement();
+
+ public class SliderTickJudgement : OsuJudgement
+ {
+ public override bool IsBonus => true;
+
+ protected override int NumericResultFor(HitResult result) => result == MaxResult ? 10 : 0;
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs
index cdea7276f3..c8fe4f41ca 100644
--- a/osu.Game.Rulesets.Osu/OsuInputManager.cs
+++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu
///
public bool AllowUserCursorMovement { get; set; } = true;
- protected override RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ protected override KeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
=> new OsuKeyBindingContainer(ruleset, variant, unique);
public OsuInputManager(RulesetInfo ruleset)
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index 148869f5e8..a2c0e051d0 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu
new KeyBinding(InputKey.MouseRight, OsuAction.RightButton),
};
- public override IEnumerable ConvertLegacyMods(LegacyMods mods)
+ public override IEnumerable ConvertFromLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
yield return new OsuModNightcore();
diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs
index e6c6db5e61..3db81d70da 100644
--- a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs
+++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs
@@ -26,11 +26,23 @@ namespace osu.Game.Rulesets.Osu.Replays
Actions.AddRange(actions);
}
- public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
+ public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
Position = currentFrame.Position;
if (currentFrame.MouseLeft) Actions.Add(OsuAction.LeftButton);
if (currentFrame.MouseRight) Actions.Add(OsuAction.RightButton);
}
+
+ public LegacyReplayFrame ToLegacy(IBeatmap beatmap)
+ {
+ ReplayButtonState state = ReplayButtonState.None;
+
+ if (Actions.Contains(OsuAction.LeftButton))
+ state |= ReplayButtonState.Left1;
+ if (Actions.Contains(OsuAction.RightButton))
+ state |= ReplayButtonState.Right1;
+
+ return new LegacyReplayFrame(Time, Position.X, Position.Y, state);
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
index ed75d47bbe..b04e3cef3b 100644
--- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
@@ -66,6 +66,8 @@ namespace osu.Game.Rulesets.Osu.UI
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuFramedReplayInputHandler(replay);
+ protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new OsuReplayRecorder(replay);
+
public override double GameplayStartTime
{
get
diff --git a/osu.Game.Rulesets.Osu/UI/OsuReplayRecorder.cs b/osu.Game.Rulesets.Osu/UI/OsuReplayRecorder.cs
new file mode 100644
index 0000000000..b68ea136d5
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/UI/OsuReplayRecorder.cs
@@ -0,0 +1,23 @@
+// 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 osu.Game.Replays;
+using osu.Game.Rulesets.Osu.Replays;
+using osu.Game.Rulesets.Replays;
+using osu.Game.Rulesets.UI;
+using osuTK;
+
+namespace osu.Game.Rulesets.Osu.UI
+{
+ public class OsuReplayRecorder : ReplayRecorder
+ {
+ public OsuReplayRecorder(Replay replay)
+ : base(replay)
+ {
+ }
+
+ protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame)
+ => new OsuReplayFrame(Time.Current, mousePosition, actions.ToArray());
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModPerfect.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModPerfect.cs
index d3be2cdf0d..26c90ad295 100644
--- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModPerfect.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModPerfect.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods
[TestCase(false)]
[TestCase(true)]
- public void TestHit(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new CentreHit { StartTime = 1000 }), shouldMiss);
+ public void TestHit(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new Hit { StartTime = 1000, Type = HitType.Centre }), shouldMiss);
[TestCase(false)]
[TestCase(true)]
diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs
index f23fd6d3f9..8c26ca70ac 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs
@@ -27,8 +27,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
{
StartTime = hitObject.StartTime,
EndTime = hitObject.GetEndTime(),
- IsRim = hitObject is RimHit,
- IsCentre = hitObject is CentreHit,
+ IsRim = (hitObject as Hit)?.Type == HitType.Rim,
+ IsCentre = (hitObject as Hit)?.Type == HitType.Centre,
IsDrumRoll = hitObject is DrumRoll,
IsSwell = hitObject is Swell,
IsStrong = ((TaikoHitObject)hitObject).IsStrong
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs
index c01eef5252..0d9e813c60 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs
@@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap
{
- HitObjects = new List { new CentreHit() },
+ HitObjects = new List { new Hit { Type = HitType.Centre } },
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty(),
diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
index cc9d6e4470..695ada3a00 100644
--- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
@@ -124,24 +124,13 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
bool isRim = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE);
strong = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
- if (isRim)
+ yield return new Hit
{
- yield return new RimHit
- {
- StartTime = j,
- Samples = currentSamples,
- IsStrong = strong
- };
- }
- else
- {
- yield return new CentreHit
- {
- StartTime = j,
- Samples = currentSamples,
- IsStrong = strong
- };
- }
+ StartTime = j,
+ Type = isRim ? HitType.Rim : HitType.Centre,
+ Samples = currentSamples,
+ IsStrong = strong
+ };
i = (i + 1) % allSamples.Count;
}
@@ -180,24 +169,13 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
{
bool isRim = samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE);
- if (isRim)
+ yield return new Hit
{
- yield return new RimHit
- {
- StartTime = obj.StartTime,
- Samples = obj.Samples,
- IsStrong = strong
- };
- }
- else
- {
- yield return new CentreHit
- {
- StartTime = obj.StartTime,
- Samples = obj.Samples,
- IsStrong = strong
- };
- }
+ StartTime = obj.StartTime,
+ Type = isRim ? HitType.Rim : HitType.Centre,
+ Samples = obj.Samples,
+ IsStrong = strong
+ };
break;
}
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs
index 24345275c1..6807142327 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate)
: base(hitObject, lastObject, clockRate)
{
- HasTypeChange = lastObject is RimHit != hitObject is RimHit;
+ HasTypeChange = (lastObject as Hit)?.Type != (hitObject as Hit)?.Type;
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs
new file mode 100644
index 0000000000..1cf19ac18e
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs
@@ -0,0 +1,27 @@
+// 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.Utils;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Taiko.Beatmaps;
+using osu.Game.Rulesets.Taiko.Objects;
+
+namespace osu.Game.Rulesets.Taiko.Mods
+{
+ public class TaikoModRandom : ModRandom, IApplicableToBeatmap
+ {
+ public override string Description => @"Shuffle around the colours!";
+
+ public void ApplyToBeatmap(IBeatmap beatmap)
+ {
+ var taikoBeatmap = (TaikoBeatmap)beatmap;
+
+ foreach (var obj in taikoBeatmap.HitObjects)
+ {
+ if (obj is Hit hit)
+ hit.Type = RNG.Next(2) == 0 ? HitType.Centre : HitType.Rim;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Objects/CentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/CentreHit.cs
deleted file mode 100644
index a6354b16ed..0000000000
--- a/osu.Game.Rulesets.Taiko/Objects/CentreHit.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-// 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.Taiko.Objects
-{
- public class CentreHit : Hit
- {
- }
-}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs
index 6cc9357580..2aca701515 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs
@@ -5,5 +5,9 @@ namespace osu.Game.Rulesets.Taiko.Objects
{
public class Hit : TaikoHitObject
{
+ ///
+ /// The that actuates this .
+ ///
+ public HitType Type { get; set; }
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/HitType.cs b/osu.Game.Rulesets.Taiko/Objects/HitType.cs
new file mode 100644
index 0000000000..17b3fdbd04
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Objects/HitType.cs
@@ -0,0 +1,21 @@
+// 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.Taiko.Objects
+{
+ ///
+ /// The type of a .
+ ///
+ public enum HitType
+ {
+ ///
+ /// A that can be hit by the centre portion of the drum.
+ ///
+ Centre,
+
+ ///
+ /// A that can be hit by the rim portion of the drum.
+ ///
+ Rim
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Objects/RimHit.cs b/osu.Game.Rulesets.Taiko/Objects/RimHit.cs
deleted file mode 100644
index 6f6b089e03..0000000000
--- a/osu.Game.Rulesets.Taiko/Objects/RimHit.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-// 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.Taiko.Objects
-{
- public class RimHit : Hit
- {
- }
-}
diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs
index 48eb33976e..273f4e4105 100644
--- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs
+++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs
@@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
{
TaikoAction[] actions;
- if (hit is CentreHit)
+ if (hit.Type == HitType.Centre)
{
actions = h.IsStrong
? new[] { TaikoAction.LeftCentre, TaikoAction.RightCentre }
diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs
index c5ebefc397..d2a7329a28 100644
--- a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs
+++ b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs
@@ -23,12 +23,24 @@ namespace osu.Game.Rulesets.Taiko.Replays
Actions.AddRange(actions);
}
- public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
+ public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
if (currentFrame.MouseRight1) Actions.Add(TaikoAction.LeftRim);
if (currentFrame.MouseRight2) Actions.Add(TaikoAction.RightRim);
if (currentFrame.MouseLeft1) Actions.Add(TaikoAction.LeftCentre);
if (currentFrame.MouseLeft2) Actions.Add(TaikoAction.RightCentre);
}
+
+ public LegacyReplayFrame ToLegacy(IBeatmap beatmap)
+ {
+ ReplayButtonState state = ReplayButtonState.None;
+
+ if (Actions.Contains(TaikoAction.LeftRim)) state |= ReplayButtonState.Right1;
+ if (Actions.Contains(TaikoAction.RightRim)) state |= ReplayButtonState.Right2;
+ if (Actions.Contains(TaikoAction.LeftCentre)) state |= ReplayButtonState.Left1;
+ if (Actions.Contains(TaikoAction.RightCentre)) state |= ReplayButtonState.Left2;
+
+ return new LegacyReplayFrame(Time, null, null, state);
+ }
}
}
diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
index fc79e59864..a6c9a33569 100644
--- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Taiko
new KeyBinding(InputKey.K, TaikoAction.RightRim),
};
- public override IEnumerable ConvertLegacyMods(LegacyMods mods)
+ public override IEnumerable ConvertFromLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
yield return new TaikoModNightcore();
@@ -114,6 +114,7 @@ namespace osu.Game.Rulesets.Taiko
case ModType.Conversion:
return new Mod[]
{
+ new TaikoModRandom(),
new TaikoModDifficultyAdjust(),
};
diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
index 0c7495aa52..e4a4b555a7 100644
--- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
@@ -48,11 +48,11 @@ namespace osu.Game.Rulesets.Taiko.UI
{
switch (h)
{
- case CentreHit centreHit:
- return new DrawableCentreHit(centreHit);
-
- case RimHit rimHit:
- return new DrawableRimHit(rimHit);
+ case Hit hit:
+ if (hit.Type == HitType.Centre)
+ return new DrawableCentreHit(hit);
+ else
+ return new DrawableRimHit(hit);
case DrumRoll drumRoll:
return new DrawableDrumRoll(drumRoll);
@@ -65,5 +65,7 @@ namespace osu.Game.Rulesets.Taiko.UI
}
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new TaikoFramedReplayInputHandler(replay);
+
+ protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new TaikoReplayRecorder(replay);
}
}
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
index a10f70a344..bde9085c23 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
@@ -14,9 +14,9 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
-using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
+using osu.Game.Rulesets.Taiko.Objects;
using osuTK;
using osuTK.Graphics;
@@ -245,7 +245,7 @@ namespace osu.Game.Rulesets.Taiko.UI
if (!result.IsHit)
break;
- bool isRim = judgedObject.HitObject is RimHit;
+ bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim;
hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs b/osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs
new file mode 100644
index 0000000000..1859dabf03
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs
@@ -0,0 +1,23 @@
+// 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 osu.Game.Replays;
+using osu.Game.Rulesets.Replays;
+using osu.Game.Rulesets.Taiko.Replays;
+using osu.Game.Rulesets.UI;
+using osuTK;
+
+namespace osu.Game.Rulesets.Taiko.UI
+{
+ public class TaikoReplayRecorder : ReplayRecorder
+ {
+ public TaikoReplayRecorder(Replay replay)
+ : base(replay)
+ {
+ }
+
+ protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame) =>
+ new TaikoReplayFrame(Time.Current, actions.ToArray());
+ }
+}
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
index 76b76aa357..2fdeadca02 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
@@ -26,34 +26,34 @@ namespace osu.Game.Tests.Beatmaps.Formats
var storyboard = decoder.Decode(stream);
Assert.IsTrue(storyboard.HasDrawable);
- Assert.AreEqual(4, storyboard.Layers.Count());
+ Assert.AreEqual(5, storyboard.Layers.Count());
StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3);
Assert.IsNotNull(background);
Assert.AreEqual(16, background.Elements.Count);
- Assert.IsTrue(background.EnabledWhenFailing);
- Assert.IsTrue(background.EnabledWhenPassing);
+ Assert.IsTrue(background.VisibleWhenFailing);
+ Assert.IsTrue(background.VisibleWhenPassing);
Assert.AreEqual("Background", background.Name);
StoryboardLayer fail = storyboard.Layers.FirstOrDefault(l => l.Depth == 2);
Assert.IsNotNull(fail);
Assert.AreEqual(0, fail.Elements.Count);
- Assert.IsTrue(fail.EnabledWhenFailing);
- Assert.IsFalse(fail.EnabledWhenPassing);
+ Assert.IsTrue(fail.VisibleWhenFailing);
+ Assert.IsFalse(fail.VisibleWhenPassing);
Assert.AreEqual("Fail", fail.Name);
StoryboardLayer pass = storyboard.Layers.FirstOrDefault(l => l.Depth == 1);
Assert.IsNotNull(pass);
Assert.AreEqual(0, pass.Elements.Count);
- Assert.IsFalse(pass.EnabledWhenFailing);
- Assert.IsTrue(pass.EnabledWhenPassing);
+ Assert.IsFalse(pass.VisibleWhenFailing);
+ Assert.IsTrue(pass.VisibleWhenPassing);
Assert.AreEqual("Pass", pass.Name);
StoryboardLayer foreground = storyboard.Layers.FirstOrDefault(l => l.Depth == 0);
Assert.IsNotNull(foreground);
Assert.AreEqual(151, foreground.Elements.Count);
- Assert.IsTrue(foreground.EnabledWhenFailing);
- Assert.IsTrue(foreground.EnabledWhenPassing);
+ Assert.IsTrue(foreground.VisibleWhenFailing);
+ Assert.IsTrue(foreground.VisibleWhenPassing);
Assert.AreEqual("Foreground", foreground.Name);
int spriteCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSprite));
diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
index c1bd73ef05..c6095ae404 100644
--- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
@@ -417,7 +417,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public async Task TestImportWithDuplicateHashes()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportNestedStructure)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWithDuplicateHashes)))
{
try
{
diff --git a/osu.Game.Tests/Gameplay/TestSceneReplayRecorder.cs b/osu.Game.Tests/Gameplay/TestSceneReplayRecorder.cs
new file mode 100644
index 0000000000..734991b868
--- /dev/null
+++ b/osu.Game.Tests/Gameplay/TestSceneReplayRecorder.cs
@@ -0,0 +1,282 @@
+// 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 NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.Bindings;
+using osu.Framework.Input.Events;
+using osu.Framework.Input.StateChanges;
+using osu.Framework.Testing;
+using osu.Framework.Threading;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Replays;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Replays;
+using osu.Game.Rulesets.UI;
+using osu.Game.Tests.Visual;
+using osu.Game.Tests.Visual.UserInterface;
+using osuTK;
+using osuTK.Graphics;
+using osuTK.Input;
+
+namespace osu.Game.Tests.Gameplay
+{
+ public class TestSceneReplayRecorder : OsuManualInputManagerTestScene
+ {
+ private TestRulesetInputManager playbackManager;
+ private TestRulesetInputManager recordingManager;
+
+ private Replay replay;
+
+ private TestReplayRecorder recorder;
+
+ [SetUp]
+ public void SetUp() => Schedule(() =>
+ {
+ replay = new Replay();
+
+ Add(new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
+ {
+ Recorder = recorder = new TestReplayRecorder(replay)
+ {
+ ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos),
+ },
+ Child = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ Colour = Color4.Brown,
+ RelativeSizeAxes = Axes.Both,
+ },
+ new OsuSpriteText
+ {
+ Text = "Recording",
+ Scale = new Vector2(3),
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ new TestInputConsumer()
+ }
+ },
+ }
+ },
+ new Drawable[]
+ {
+ playbackManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
+ {
+ ReplayInputHandler = new TestFramedReplayInputHandler(replay)
+ {
+ GamefieldToScreenSpace = pos => playbackManager.ToScreenSpace(pos),
+ },
+ Child = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ Colour = Color4.DarkBlue,
+ RelativeSizeAxes = Axes.Both,
+ },
+ new OsuSpriteText
+ {
+ Text = "Playback",
+ Scale = new Vector2(3),
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ new TestInputConsumer()
+ }
+ },
+ }
+ }
+ }
+ });
+ });
+
+ [Test]
+ public void TestBasic()
+ {
+ AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
+ AddUntilStep("one frame recorded", () => replay.Frames.Count == 1);
+ AddAssert("position matches", () => playbackManager.ChildrenOfType().First().Position == recordingManager.ChildrenOfType().First().Position);
+ }
+
+ [Test]
+ public void TestHighFrameRate()
+ {
+ ScheduledDelegate moveFunction = null;
+
+ AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
+ AddStep("much move", () => moveFunction = Scheduler.AddDelayed(() =>
+ InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(-1, 0)), 10, true));
+ AddWaitStep("move", 10);
+ AddStep("stop move", () => moveFunction.Cancel());
+ AddAssert("at least 60 frames recorded", () => replay.Frames.Count > 60);
+ }
+
+ [Test]
+ public void TestLimitedFrameRate()
+ {
+ ScheduledDelegate moveFunction = null;
+
+ AddStep("lower rate", () => recorder.RecordFrameRate = 2);
+ AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
+ AddStep("much move", () => moveFunction = Scheduler.AddDelayed(() =>
+ InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(-1, 0)), 10, true));
+ AddWaitStep("move", 10);
+ AddStep("stop move", () => moveFunction.Cancel());
+ AddAssert("less than 10 frames recorded", () => replay.Frames.Count < 10);
+ }
+
+ [Test]
+ public void TestLimitedFrameRateWithImportantFrames()
+ {
+ ScheduledDelegate moveFunction = null;
+
+ AddStep("lower rate", () => recorder.RecordFrameRate = 2);
+ AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
+ AddStep("much move with press", () => moveFunction = Scheduler.AddDelayed(() =>
+ {
+ InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(-1, 0));
+ InputManager.PressButton(MouseButton.Left);
+ InputManager.ReleaseButton(MouseButton.Left);
+ }, 10, true));
+ AddWaitStep("move", 10);
+ AddStep("stop move", () => moveFunction.Cancel());
+ AddAssert("at least 60 frames recorded", () => replay.Frames.Count > 60);
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+ playbackManager?.ReplayInputHandler.SetFrameFromTime(Time.Current - 100);
+ }
+
+ public class TestFramedReplayInputHandler : FramedReplayInputHandler
+ {
+ public TestFramedReplayInputHandler(Replay replay)
+ : base(replay)
+ {
+ }
+
+ public override List GetPendingInputs()
+ {
+ return new List
+ {
+ new MousePositionAbsoluteInput
+ {
+ Position = GamefieldToScreenSpace(CurrentFrame?.Position ?? Vector2.Zero)
+ },
+ new ReplayState
+ {
+ PressedActions = CurrentFrame?.Actions ?? new List()
+ }
+ };
+ }
+ }
+
+ public class TestInputConsumer : CompositeDrawable, IKeyBindingHandler
+ {
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent.ReceivePositionalInputAt(screenSpacePos);
+
+ private readonly Box box;
+
+ public TestInputConsumer()
+ {
+ Size = new Vector2(30);
+
+ Origin = Anchor.Centre;
+
+ InternalChildren = new Drawable[]
+ {
+ box = new Box
+ {
+ Colour = Color4.Black,
+ RelativeSizeAxes = Axes.Both,
+ },
+ };
+ }
+
+ protected override bool OnMouseMove(MouseMoveEvent e)
+ {
+ Position = e.MousePosition;
+ return base.OnMouseMove(e);
+ }
+
+ public bool OnPressed(TestAction action)
+ {
+ box.Colour = Color4.White;
+ return true;
+ }
+
+ public void OnReleased(TestAction action)
+ {
+ box.Colour = Color4.Black;
+ }
+ }
+
+ public class TestRulesetInputManager : RulesetInputManager
+ {
+ public TestRulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ : base(ruleset, variant, unique)
+ {
+ }
+
+ protected override KeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ => new TestKeyBindingContainer();
+
+ internal class TestKeyBindingContainer : KeyBindingContainer
+ {
+ public override IEnumerable DefaultKeyBindings => new[]
+ {
+ new KeyBinding(InputKey.MouseLeft, TestAction.Down),
+ };
+ }
+ }
+
+ public class TestReplayFrame : ReplayFrame
+ {
+ public Vector2 Position;
+
+ public List Actions = new List();
+
+ public TestReplayFrame(double time, Vector2 position, params TestAction[] actions)
+ : base(time)
+ {
+ Position = position;
+ Actions.AddRange(actions);
+ }
+ }
+
+ public enum TestAction
+ {
+ Down,
+ }
+
+ internal class TestReplayRecorder : ReplayRecorder
+ {
+ public TestReplayRecorder(Replay target)
+ : base(target)
+ {
+ }
+
+ protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame)
+ => new TestReplayFrame(Time.Current, mousePosition, actions.ToArray());
+ }
+ }
+}
diff --git a/osu.Game.Tests/Gameplay/TestSceneReplayRecording.cs b/osu.Game.Tests/Gameplay/TestSceneReplayRecording.cs
new file mode 100644
index 0000000000..057d026132
--- /dev/null
+++ b/osu.Game.Tests/Gameplay/TestSceneReplayRecording.cs
@@ -0,0 +1,221 @@
+// 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 osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.Bindings;
+using osu.Framework.Input.Events;
+using osu.Framework.Input.StateChanges;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Replays;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Replays;
+using osu.Game.Rulesets.UI;
+using osu.Game.Tests.Visual;
+using osu.Game.Tests.Visual.UserInterface;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Tests.Gameplay
+{
+ public class TestSceneReplayRecording : OsuTestScene
+ {
+ private readonly TestRulesetInputManager playbackManager;
+
+ private readonly TestRulesetInputManager recordingManager;
+
+ public TestSceneReplayRecording()
+ {
+ Replay replay = new Replay();
+
+ Add(new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
+ {
+ Recorder = new TestReplayRecorder(replay)
+ {
+ ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos)
+ },
+ Child = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ Colour = Color4.Brown,
+ RelativeSizeAxes = Axes.Both,
+ },
+ new OsuSpriteText
+ {
+ Text = "Recording",
+ Scale = new Vector2(3),
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ new TestConsumer()
+ }
+ },
+ }
+ },
+ new Drawable[]
+ {
+ playbackManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
+ {
+ ReplayInputHandler = new TestFramedReplayInputHandler(replay)
+ {
+ GamefieldToScreenSpace = pos => playbackManager.ToScreenSpace(pos),
+ },
+ Child = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ Colour = Color4.DarkBlue,
+ RelativeSizeAxes = Axes.Both,
+ },
+ new OsuSpriteText
+ {
+ Text = "Playback",
+ Scale = new Vector2(3),
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ new TestConsumer()
+ }
+ },
+ }
+ }
+ }
+ });
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ playbackManager.ReplayInputHandler.SetFrameFromTime(Time.Current - 500);
+ }
+ }
+
+ public class TestFramedReplayInputHandler : FramedReplayInputHandler
+ {
+ public TestFramedReplayInputHandler(Replay replay)
+ : base(replay)
+ {
+ }
+
+ public override List GetPendingInputs()
+ {
+ return new List
+ {
+ new MousePositionAbsoluteInput
+ {
+ Position = GamefieldToScreenSpace(CurrentFrame?.Position ?? Vector2.Zero)
+ },
+ new ReplayState
+ {
+ PressedActions = CurrentFrame?.Actions ?? new List()
+ }
+ };
+ }
+ }
+
+ public class TestConsumer : CompositeDrawable, IKeyBindingHandler
+ {
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent.ReceivePositionalInputAt(screenSpacePos);
+
+ private readonly Box box;
+
+ public TestConsumer()
+ {
+ Size = new Vector2(30);
+
+ Origin = Anchor.Centre;
+
+ InternalChildren = new Drawable[]
+ {
+ box = new Box
+ {
+ Colour = Color4.Black,
+ RelativeSizeAxes = Axes.Both,
+ },
+ };
+ }
+
+ protected override bool OnMouseMove(MouseMoveEvent e)
+ {
+ Position = e.MousePosition;
+ return base.OnMouseMove(e);
+ }
+
+ public bool OnPressed(TestAction action)
+ {
+ box.Colour = Color4.White;
+ return true;
+ }
+
+ public void OnReleased(TestAction action)
+ {
+ box.Colour = Color4.Black;
+ }
+ }
+
+ public class TestRulesetInputManager : RulesetInputManager
+ {
+ public TestRulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ : base(ruleset, variant, unique)
+ {
+ }
+
+ protected override KeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ => new TestKeyBindingContainer();
+
+ internal class TestKeyBindingContainer : KeyBindingContainer
+ {
+ public override IEnumerable DefaultKeyBindings => new[]
+ {
+ new KeyBinding(InputKey.MouseLeft, TestAction.Down),
+ };
+ }
+ }
+
+ public class TestReplayFrame : ReplayFrame
+ {
+ public Vector2 Position;
+
+ public List Actions = new List();
+
+ public TestReplayFrame(double time, Vector2 position, params TestAction[] actions)
+ : base(time)
+ {
+ Position = position;
+ Actions.AddRange(actions);
+ }
+ }
+
+ public enum TestAction
+ {
+ Down,
+ }
+
+ internal class TestReplayRecorder : ReplayRecorder
+ {
+ public TestReplayRecorder(Replay target)
+ : base(target)
+ {
+ }
+
+ protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame) =>
+ new TestReplayFrame(Time.Current, mousePosition, actions.ToArray());
+ }
+}
diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
index a139c3a8c2..90bf419644 100644
--- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
+++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
@@ -236,8 +236,6 @@ namespace osu.Game.Tests.Scores.IO
}
public override IEnumerable Filenames => new[] { "test_file.osr" };
-
- public override Stream GetUnderlyingStream() => new MemoryStream();
}
}
}
diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs
index 06a155e78b..f97aa48f11 100644
--- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs
+++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs
@@ -27,6 +27,7 @@ using osu.Game.Screens;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.PlayerSettings;
+using osu.Game.Screens.Ranking;
using osu.Game.Screens.Select;
using osu.Game.Tests.Resources;
using osu.Game.Users;
@@ -36,7 +37,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Background
{
[TestFixture]
- public class TestSceneUserDimBackgrounds : ManualInputManagerTestScene
+ public class TestSceneUserDimBackgrounds : OsuManualInputManagerTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
@@ -203,7 +204,7 @@ namespace osu.Game.Tests.Visual.Background
}
///
- /// Check if the visual settings container removes user dim when suspending for
+ /// Check if the visual settings container removes user dim when suspending for
///
[Test]
public void TransitionTest()
@@ -277,6 +278,7 @@ namespace osu.Game.Tests.Visual.Background
private void setupUserSettings()
{
+ AddUntilStep("Song select is current", () => songSelect.IsCurrentScreen());
AddUntilStep("Song select has selection", () => songSelect.Carousel?.SelectedBeatmap != null);
AddStep("Set default user settings", () =>
{
@@ -335,7 +337,7 @@ namespace osu.Game.Tests.Visual.Background
public bool IsBackgroundCurrent() => ((FadeAccessibleBackground)Background).IsCurrentScreen();
}
- private class FadeAccessibleResults : SoloResults
+ private class FadeAccessibleResults : ResultsScreen
{
public FadeAccessibleResults(ScoreInfo score)
: base(score)
diff --git a/osu.Game.Tests/Visual/Components/TestSceneIdleTracker.cs b/osu.Game.Tests/Visual/Components/TestSceneIdleTracker.cs
index 55aaeed8bf..4d64c7d35d 100644
--- a/osu.Game.Tests/Visual/Components/TestSceneIdleTracker.cs
+++ b/osu.Game.Tests/Visual/Components/TestSceneIdleTracker.cs
@@ -12,7 +12,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Components
{
[TestFixture]
- public class TestSceneIdleTracker : ManualInputManagerTestScene
+ public class TestSceneIdleTracker : OsuManualInputManagerTestScene
{
private IdleTrackingBox box1;
private IdleTrackingBox box2;
diff --git a/osu.Game.Tests/Visual/Editor/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editor/TestSceneBeatDivisorControl.cs
index 7531a7be2c..fd7a5980f3 100644
--- a/osu.Game.Tests/Visual/Editor/TestSceneBeatDivisorControl.cs
+++ b/osu.Game.Tests/Visual/Editor/TestSceneBeatDivisorControl.cs
@@ -3,27 +3,83 @@
using System;
using System.Collections.Generic;
-using osu.Framework.Allocation;
+using System.Linq;
+using NUnit.Framework;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Testing;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components;
using osuTK;
+using osuTK.Input;
namespace osu.Game.Tests.Visual.Editor
{
- public class TestSceneBeatDivisorControl : OsuTestScene
+ public class TestSceneBeatDivisorControl : OsuManualInputManagerTestScene
{
public override IReadOnlyList RequiredTypes => new[] { typeof(BindableBeatDivisor) };
+ private BeatDivisorControl beatDivisorControl;
+ private BindableBeatDivisor bindableBeatDivisor;
- [BackgroundDependencyLoader]
- private void load()
+ private SliderBar tickSliderBar;
+ private EquilateralTriangle tickMarkerHead;
+
+ [SetUp]
+ public void SetUp() => Schedule(() =>
{
- Child = new BeatDivisorControl(new BindableBeatDivisor())
+ Child = beatDivisorControl = new BeatDivisorControl(bindableBeatDivisor = new BindableBeatDivisor(16))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(90, 90)
};
+
+ tickSliderBar = beatDivisorControl.ChildrenOfType>().Single();
+ tickMarkerHead = tickSliderBar.ChildrenOfType().Single();
+ });
+
+ [Test]
+ public void TestBindableBeatDivisor()
+ {
+ AddRepeatStep("move previous", () => bindableBeatDivisor.Previous(), 4);
+ AddAssert("divisor is 4", () => bindableBeatDivisor.Value == 4);
+ AddRepeatStep("move next", () => bindableBeatDivisor.Next(), 3);
+ AddAssert("divisor is 12", () => bindableBeatDivisor.Value == 12);
+ }
+
+ [Test]
+ public void TestMouseInput()
+ {
+ AddStep("hold marker", () =>
+ {
+ InputManager.MoveMouseTo(tickMarkerHead.ScreenSpaceDrawQuad.Centre);
+ InputManager.PressButton(MouseButton.Left);
+ });
+ AddStep("move to 8 and release", () =>
+ {
+ InputManager.MoveMouseTo(tickSliderBar.ScreenSpaceDrawQuad.Centre);
+ InputManager.ReleaseButton(MouseButton.Left);
+ });
+ AddAssert("divisor is 8", () => bindableBeatDivisor.Value == 8);
+ AddStep("hold marker", () => InputManager.PressButton(MouseButton.Left));
+ AddStep("move to 16", () => InputManager.MoveMouseTo(getPositionForDivisor(16)));
+ AddStep("move to ~10 and release", () =>
+ {
+ InputManager.MoveMouseTo(getPositionForDivisor(10));
+ InputManager.ReleaseButton(MouseButton.Left);
+ });
+ AddAssert("divisor clamped to 8", () => bindableBeatDivisor.Value == 8);
+ }
+
+ private Vector2 getPositionForDivisor(int divisor)
+ {
+ var relativePosition = (float)Math.Clamp(divisor, 0, 16) / 16;
+ var sliderDrawQuad = tickSliderBar.ScreenSpaceDrawQuad;
+ return new Vector2(
+ sliderDrawQuad.TopLeft.X + sliderDrawQuad.Width * relativePosition,
+ sliderDrawQuad.Centre.Y
+ );
}
}
}
diff --git a/osu.Game.Tests/Visual/Editor/TestSceneZoomableScrollContainer.cs b/osu.Game.Tests/Visual/Editor/TestSceneZoomableScrollContainer.cs
index fd248abbc9..19d19c2759 100644
--- a/osu.Game.Tests/Visual/Editor/TestSceneZoomableScrollContainer.cs
+++ b/osu.Game.Tests/Visual/Editor/TestSceneZoomableScrollContainer.cs
@@ -17,7 +17,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor
{
- public class TestSceneZoomableScrollContainer : ManualInputManagerTestScene
+ public class TestSceneZoomableScrollContainer : OsuManualInputManagerTestScene
{
private ZoomableScrollContainer scrollContainer;
private Drawable innerBox;
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs
index c1635ffc83..ea3e0c2293 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs
@@ -18,7 +18,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
{
[Description("player pause/fail screens")]
- public class TestSceneGameplayMenuOverlay : ManualInputManagerTestScene
+ public class TestSceneGameplayMenuOverlay : OsuManualInputManagerTestScene
{
public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseOverlay) };
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
index fc03dc6ed3..c192a7b0e0 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
@@ -15,7 +15,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
{
- public class TestSceneHUDOverlay : ManualInputManagerTestScene
+ public class TestSceneHUDOverlay : OsuManualInputManagerTestScene
{
private HUDOverlay hudOverlay;
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHoldForMenuButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHoldForMenuButton.cs
index 0c5ead10cf..235842acc9 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneHoldForMenuButton.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHoldForMenuButton.cs
@@ -13,7 +13,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
{
[Description("'Hold to Quit' UI element")]
- public class TestSceneHoldForMenuButton : ManualInputManagerTestScene
+ public class TestSceneHoldForMenuButton : OsuManualInputManagerTestScene
{
private bool exitAction;
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs
index 227ada70fe..593dcd245c 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs
@@ -13,7 +13,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
{
[TestFixture]
- public class TestSceneKeyCounter : ManualInputManagerTestScene
+ public class TestSceneKeyCounter : OsuManualInputManagerTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
index 175f909a5a..4c73065087 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
@@ -29,7 +29,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
{
- public class TestScenePlayerLoader : ManualInputManagerTestScene
+ public class TestScenePlayerLoader : OsuManualInputManagerTestScene
{
private TestPlayerLoader loader;
private TestPlayerLoaderContainer container;
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs
index 8cb44de8cb..c9561a70fa 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs
@@ -11,7 +11,7 @@ using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Rulesets;
-using osu.Game.Screens.Ranking.Pages;
+using osu.Game.Screens.Ranking;
namespace osu.Game.Tests.Visual.Gameplay
{
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs
index 4c5c18f38a..6a0f86fe53 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkipOverlay.cs
@@ -14,7 +14,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
{
[TestFixture]
- public class TestSceneSkipOverlay : ManualInputManagerTestScene
+ public class TestSceneSkipOverlay : OsuManualInputManagerTestScene
{
private SkipOverlay skip;
private int requestCount;
diff --git a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs
index 5870ef9813..1ad4d9dca9 100644
--- a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs
+++ b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs
@@ -64,6 +64,8 @@ namespace osu.Game.Tests.Visual.Menus
introStack.Push(CreateScreen());
});
+
+ AddUntilStep("wait for menu", () => introStack.CurrentScreen is MainMenu);
}
protected abstract IScreen CreateScreen();
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneLoaderAnimation.cs b/osu.Game.Tests/Visual/Menus/TestSceneLoader.cs
similarity index 77%
rename from osu.Game.Tests/Visual/Menus/TestSceneLoaderAnimation.cs
rename to osu.Game.Tests/Visual/Menus/TestSceneLoader.cs
index 61fed3013e..b3064ba9be 100644
--- a/osu.Game.Tests/Visual/Menus/TestSceneLoaderAnimation.cs
+++ b/osu.Game.Tests/Visual/Menus/TestSceneLoader.cs
@@ -1,12 +1,15 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Linq;
using System.Threading;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Screens;
+using osu.Framework.Testing;
+using osu.Game.Graphics.UserInterface;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osuTK.Graphics;
@@ -14,14 +17,14 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Menus
{
[TestFixture]
- public class TestSceneLoaderAnimation : ScreenTestScene
+ public class TestSceneLoader : ScreenTestScene
{
private TestLoader loader;
[Cached]
private OsuLogo logo;
- public TestSceneLoaderAnimation()
+ public TestSceneLoader()
{
Child = logo = new OsuLogo
{
@@ -42,33 +45,33 @@ namespace osu.Game.Tests.Visual.Menus
LoadScreen(loader);
});
+
+ AddAssert("spinner did not display", () => loader.LoadingSpinner?.Alpha == 0);
+
+ AddUntilStep("loaded", () => loader.ScreenLoaded);
+ AddUntilStep("not current", () => !loader.IsCurrentScreen());
}
[Test]
public void TestDelayedLoad()
{
AddStep("begin loading", () => LoadScreen(loader = new TestLoader()));
- AddUntilStep("wait for logo visible", () => loader.Logo?.Alpha > 0);
+ AddUntilStep("wait for spinner visible", () => loader.LoadingSpinner?.Alpha > 0);
AddStep("finish loading", () => loader.AllowLoad.Set());
- AddUntilStep("loaded", () => loader.Logo != null && loader.ScreenLoaded);
- AddUntilStep("logo gone", () => loader.Logo?.Alpha == 0);
+ AddUntilStep("spinner gone", () => loader.LoadingSpinner?.Alpha == 0);
+ AddUntilStep("loaded", () => loader.ScreenLoaded);
+ AddUntilStep("not current", () => !loader.IsCurrentScreen());
}
private class TestLoader : Loader
{
public readonly ManualResetEventSlim AllowLoad = new ManualResetEventSlim();
- public OsuLogo Logo;
+ public LoadingSpinner LoadingSpinner => this.ChildrenOfType().FirstOrDefault();
private TestScreen screen;
public bool ScreenLoaded => screen.IsCurrentScreen();
- protected override void LogoArriving(OsuLogo logo, bool resuming)
- {
- Logo = logo;
- base.LogoArriving(logo, resuming);
- }
-
protected override OsuScreen CreateLoadableScreen() => screen = new TestScreen();
protected override ShaderPrecompiler CreateShaderPrecompiler() => new TestShaderPrecompiler(AllowLoad);
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs
index 9fbe8f7ffe..713ba13439 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs
@@ -20,7 +20,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Multiplayer
{
- public class TestSceneDrawableRoomPlaylist : ManualInputManagerTestScene
+ public class TestSceneDrawableRoomPlaylist : OsuManualInputManagerTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchResults.cs
deleted file mode 100644
index 58e9240026..0000000000
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchResults.cs
+++ /dev/null
@@ -1,106 +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 System;
-using System.Collections.Generic;
-using System.Linq;
-using osu.Framework.Allocation;
-using osu.Game.Beatmaps;
-using osu.Game.Online.API;
-using osu.Game.Online.API.Requests.Responses;
-using osu.Game.Scoring;
-using osu.Game.Screens.Multi.Match.Components;
-using osu.Game.Screens.Multi.Ranking;
-using osu.Game.Screens.Multi.Ranking.Pages;
-using osu.Game.Screens.Multi.Ranking.Types;
-using osu.Game.Screens.Ranking;
-using osu.Game.Users;
-
-namespace osu.Game.Tests.Visual.Multiplayer
-{
- public class TestSceneMatchResults : MultiplayerTestScene
- {
- public override IReadOnlyList RequiredTypes => new[]
- {
- typeof(MatchResults),
- typeof(RoomLeaderboardPageInfo),
- typeof(RoomLeaderboardPage)
- };
-
- [Resolved]
- private BeatmapManager beatmaps { get; set; }
-
- [BackgroundDependencyLoader]
- private void load()
- {
- var beatmapInfo = beatmaps.QueryBeatmap(b => b.RulesetID == 0);
- if (beatmapInfo != null)
- Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
-
- Room.RoomID.Value = 1;
- Room.Name.Value = "an awesome room";
-
- LoadScreen(new TestMatchResults(new ScoreInfo
- {
- User = new User { Id = 10 },
- }));
- }
-
- private class TestMatchResults : MatchResults
- {
- public TestMatchResults(ScoreInfo score)
- : base(score)
- {
- }
-
- protected override IEnumerable CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap.Value) };
- }
-
- private class TestRoomLeaderboardPageInfo : RoomLeaderboardPageInfo
- {
- private readonly ScoreInfo score;
- private readonly WorkingBeatmap beatmap;
-
- public TestRoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap)
- : base(score, beatmap)
- {
- this.score = score;
- this.beatmap = beatmap;
- }
-
- public override ResultsPage CreatePage() => new TestRoomLeaderboardPage(score, beatmap);
- }
-
- private class TestRoomLeaderboardPage : RoomLeaderboardPage
- {
- public TestRoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap)
- : base(score, beatmap)
- {
- }
-
- protected override MatchLeaderboard CreateLeaderboard() => new TestMatchLeaderboard();
- }
-
- private class TestMatchLeaderboard : RoomLeaderboardPage.ResultsMatchLeaderboard
- {
- protected override APIRequest FetchScores(Action> scoresCallback)
- {
- var scores = Enumerable.Range(0, 50).Select(createRoomScore).ToArray();
-
- scoresCallback?.Invoke(scores);
- ScoresLoaded?.Invoke(scores);
-
- return null;
- }
-
- private APIUserScoreAggregate createRoomScore(int id) => new APIUserScoreAggregate
- {
- User = new User { Id = id, Username = $"User {id}" },
- Accuracy = 0.98,
- TotalScore = 987654,
- TotalAttempts = 13,
- CompletedBeatmaps = 5
- };
- }
- }
-}
diff --git a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs
index 0d64eb651f..31afce86ae 100644
--- a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs
+++ b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Navigation
///
/// A scene which tests full game flow.
///
- public abstract class OsuGameTestScene : ManualInputManagerTestScene
+ public abstract class OsuGameTestScene : OsuManualInputManagerTestScene
{
private GameHost host;
diff --git a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs
index 31eab7f74e..a53a818065 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs
@@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual.Online
API.Logout();
localUser = API.LocalUser.GetBoundCopy();
- localUser.BindValueChanged(user => { userPanelArea.Child = new UserPanel(user.NewValue) { Width = 200 }; }, true);
+ localUser.BindValueChanged(user => { userPanelArea.Child = new UserGridPanel(user.NewValue) { Width = 200 }; }, true);
AddStep("logout", API.Logout);
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
index 19bdaff6ff..14924dda21 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
@@ -22,7 +22,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Online
{
- public class TestSceneChatOverlay : ManualInputManagerTestScene
+ public class TestSceneChatOverlay : OsuManualInputManagerTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
@@ -38,8 +38,13 @@ namespace osu.Game.Tests.Visual.Online
private TestChatOverlay chatOverlay;
private ChannelManager channelManager;
+ private IEnumerable visibleChannels => chatOverlay.ChannelTabControl.VisibleItems.Where(channel => channel.Name != "+");
+ private IEnumerable joinedChannels => chatOverlay.ChannelTabControl.Items.Where(channel => channel.Name != "+");
private readonly List channels;
+ private Channel currentChannel => channelManager.CurrentChannel.Value;
+ private Channel nextChannel => joinedChannels.ElementAt(joinedChannels.ToList().IndexOf(currentChannel) + 1);
+ private Channel previousChannel => joinedChannels.ElementAt(joinedChannels.ToList().IndexOf(currentChannel) - 1);
private Channel channel1 => channels[0];
private Channel channel2 => channels[1];
@@ -49,7 +54,8 @@ namespace osu.Game.Tests.Visual.Online
.Select(index => new Channel(new User())
{
Name = $"Channel no. {index}",
- Topic = index == 3 ? null : $"We talk about the number {index} here"
+ Topic = index == 3 ? null : $"We talk about the number {index} here",
+ Type = index % 2 == 0 ? ChannelType.PM : ChannelType.Temporary
})
.ToList();
}
@@ -91,32 +97,15 @@ namespace osu.Game.Tests.Visual.Online
AddStep("Join channel 1", () => channelManager.JoinChannel(channel1));
AddStep("Switch to channel 1", () => clickDrawable(chatOverlay.TabMap[channel1]));
- AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1);
+ AddAssert("Current channel is channel 1", () => currentChannel == channel1);
AddAssert("Channel selector was closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
}
- [Test]
- public void TestCloseChannelWhileSelectorClosed()
- {
- AddStep("Join channel 1", () => channelManager.JoinChannel(channel1));
- AddStep("Join channel 2", () => channelManager.JoinChannel(channel2));
-
- AddStep("Switch to channel 2", () => clickDrawable(chatOverlay.TabMap[channel2]));
- AddStep("Close channel 2", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child));
-
- AddAssert("Selector remained closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
- AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1);
-
- AddStep("Close channel 1", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child));
-
- AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
- }
-
[Test]
public void TestSearchInSelector()
{
- AddStep("search for 'no. 2'", () => chatOverlay.ChildrenOfType().First().Text = "no. 2");
- AddUntilStep("only channel 2 visible", () =>
+ AddStep("Search for 'no. 2'", () => chatOverlay.ChildrenOfType().First().Text = "no. 2");
+ AddUntilStep("Only channel 2 visible", () =>
{
var listItems = chatOverlay.ChildrenOfType().Where(c => c.IsPresent);
return listItems.Count() == 1 && listItems.Single().Channel == channel2;
@@ -126,24 +115,116 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestChannelShortcutKeys()
{
- AddStep("join 10 channels", () => channels.ForEach(channel => channelManager.JoinChannel(channel)));
- AddStep("close channel selector", () =>
+ AddStep("Join channels", () => channels.ForEach(channel => channelManager.JoinChannel(channel)));
+ AddStep("Close channel selector", () =>
{
InputManager.PressKey(Key.Escape);
InputManager.ReleaseKey(Key.Escape);
});
- AddUntilStep("wait for close", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
+ AddUntilStep("Wait for close", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
for (int zeroBasedIndex = 0; zeroBasedIndex < 10; ++zeroBasedIndex)
{
var oneBasedIndex = zeroBasedIndex + 1;
var targetNumberKey = oneBasedIndex % 10;
var targetChannel = channels[zeroBasedIndex];
- AddStep($"press Alt+{targetNumberKey}", () => pressChannelHotkey(targetNumberKey));
- AddAssert($"channel #{oneBasedIndex} is selected", () => channelManager.CurrentChannel.Value == targetChannel);
+ AddStep($"Press Alt+{targetNumberKey}", () => pressChannelHotkey(targetNumberKey));
+ AddAssert($"Channel #{oneBasedIndex} is selected", () => currentChannel == targetChannel);
}
}
+ private Channel expectedChannel;
+
+ [Test]
+ public void TestCloseChannelBehaviour()
+ {
+ AddUntilStep("Join until dropdown has channels", () =>
+ {
+ if (visibleChannels.Count() < joinedChannels.Count())
+ return true;
+
+ // Using temporary channels because they don't hide their names when not active
+ channelManager.JoinChannel(new Channel
+ {
+ Name = $"Channel no. {joinedChannels.Count() + 11}",
+ Type = ChannelType.Temporary
+ });
+
+ return false;
+ });
+
+ AddStep("Switch to last tab", () => clickDrawable(chatOverlay.TabMap[visibleChannels.Last()]));
+ AddAssert("Last visible selected", () => currentChannel == visibleChannels.Last());
+
+ // Closing the last channel before dropdown
+ AddStep("Close current channel", () =>
+ {
+ expectedChannel = nextChannel;
+ chatOverlay.ChannelTabControl.RemoveChannel(currentChannel);
+ });
+ AddAssert("Next channel selected", () => currentChannel == expectedChannel);
+ AddAssert("Selector remained closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
+
+ // Depending on the window size, one more channel might need to be closed for the selectorTab to appear
+ AddUntilStep("Close channels until selector visible", () =>
+ {
+ if (chatOverlay.ChannelTabControl.VisibleItems.Last().Name == "+")
+ return true;
+
+ chatOverlay.ChannelTabControl.RemoveChannel(visibleChannels.Last());
+ return false;
+ });
+ AddAssert("Last visible selected", () => currentChannel == visibleChannels.Last());
+
+ // Closing the last channel with dropdown no longer present
+ AddStep("Close last when selector next", () =>
+ {
+ expectedChannel = previousChannel;
+ chatOverlay.ChannelTabControl.RemoveChannel(currentChannel);
+ });
+ AddAssert("Previous channel selected", () => currentChannel == expectedChannel);
+
+ // Standard channel closing
+ AddStep("Switch to previous channel", () => chatOverlay.ChannelTabControl.SwitchTab(-1));
+ AddStep("Close current channel", () =>
+ {
+ expectedChannel = nextChannel;
+ chatOverlay.ChannelTabControl.RemoveChannel(currentChannel);
+ });
+ AddAssert("Next channel selected", () => currentChannel == expectedChannel);
+
+ // Selector reappearing after all channels closed
+ AddUntilStep("Close all channels", () =>
+ {
+ if (!joinedChannels.Any())
+ return true;
+
+ chatOverlay.ChannelTabControl.RemoveChannel(joinedChannels.Last());
+ return false;
+ });
+ AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
+ }
+
+ [Test]
+ public void TestChannelCloseButton()
+ {
+ AddStep("Join 2 channels", () =>
+ {
+ channelManager.JoinChannel(channel1);
+ channelManager.JoinChannel(channel2);
+ });
+
+ // PM channel close button only appears when active
+ AddStep("Select PM channel", () => clickDrawable(chatOverlay.TabMap[channel2]));
+ AddStep("Click PM close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child));
+ AddAssert("PM channel closed", () => !channelManager.JoinedChannels.Contains(channel2));
+
+ // Non-PM chat channel close button only appears when hovered
+ AddStep("Hover normal channel tab", () => InputManager.MoveMouseTo(chatOverlay.TabMap[channel1]));
+ AddStep("Click normal close button", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child));
+ AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any());
+ }
+
private void pressChannelHotkey(int number)
{
var channelKey = Key.Number0 + number;
@@ -187,6 +268,8 @@ namespace osu.Game.Tests.Visual.Online
{
public Visibility SelectionOverlayState => ChannelSelectionOverlay.State.Value;
+ public new ChannelTabControl ChannelTabControl => base.ChannelTabControl;
+
public new ChannelSelectionOverlay ChannelSelectionOverlay => base.ChannelSelectionOverlay;
protected override ChannelTabControl CreateChannelTabControl() => new TestTabControl();
@@ -196,12 +279,22 @@ namespace osu.Game.Tests.Visual.Online
private class TestTabControl : ChannelTabControl
{
- protected override TabItem CreateTabItem(Channel value) => new TestChannelTabItem(value);
+ protected override TabItem CreateTabItem(Channel value)
+ {
+ switch (value.Type)
+ {
+ case ChannelType.PM:
+ return new TestPrivateChannelTabItem(value);
+
+ default:
+ return new TestChannelTabItem(value);
+ }
+ }
public new IReadOnlyDictionary> TabMap => base.TabMap;
}
- private class TestChannelTabItem : PrivateChannelTabItem
+ private class TestChannelTabItem : ChannelTabItem
{
public TestChannelTabItem(Channel channel)
: base(channel)
@@ -210,5 +303,15 @@ namespace osu.Game.Tests.Visual.Online
public new ClickableContainer CloseButton => base.CloseButton;
}
+
+ private class TestPrivateChannelTabItem : PrivateChannelTabItem
+ {
+ public TestPrivateChannelTabItem(Channel channel)
+ : base(channel)
+ {
+ }
+
+ public new ClickableContainer CloseButton => base.CloseButton;
+ }
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs
new file mode 100644
index 0000000000..cf365a7614
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs
@@ -0,0 +1,87 @@
+// 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.Containers;
+using osu.Game.Overlays.Dashboard.Friends;
+using osu.Framework.Graphics;
+using osu.Game.Users;
+using osu.Game.Overlays;
+using osu.Framework.Allocation;
+using NUnit.Framework;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneFriendDisplay : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(FriendDisplay),
+ typeof(FriendOnlineStreamControl),
+ typeof(UserListToolbar)
+ };
+
+ protected override bool UseOnlineAPI => true;
+
+ [Cached]
+ private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
+
+ private FriendDisplay display;
+
+ [SetUp]
+ public void Setup() => Schedule(() =>
+ {
+ Child = new BasicScrollContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = display = new FriendDisplay()
+ };
+ });
+
+ [Test]
+ public void TestOffline()
+ {
+ AddStep("Populate", () => display.Users = getUsers());
+ }
+
+ [Test]
+ public void TestOnline()
+ {
+ AddStep("Fetch online", () => display?.Fetch());
+ }
+
+ private List getUsers() => new List
+ {
+ new User
+ {
+ Username = "flyte",
+ Id = 3103765,
+ IsOnline = true,
+ CurrentModeRank = 1111,
+ Country = new Country { FlagName = "JP" },
+ CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
+ },
+ new User
+ {
+ Username = "peppy",
+ Id = 2,
+ IsOnline = false,
+ CurrentModeRank = 2222,
+ Country = new Country { FlagName = "AU" },
+ CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
+ IsSupporter = true,
+ SupportLevel = 3,
+ },
+ new User
+ {
+ Username = "Evast",
+ Id = 8195163,
+ Country = new Country { FlagName = "BY" },
+ CoverUrl = "https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg",
+ IsOnline = false,
+ LastVisit = DateTimeOffset.Now
+ }
+ };
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs
index dbd7544b38..24341cbd05 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs
@@ -18,10 +18,9 @@ namespace osu.Game.Tests.Visual.Online
public override IReadOnlyList RequiredTypes => new[]
{
typeof(UserPanel),
- typeof(SocialPanel),
typeof(FilterControl),
- typeof(SocialGridPanel),
- typeof(SocialListPanel)
+ typeof(UserGridPanel),
+ typeof(UserListPanel)
};
public TestSceneSocialOverlay()
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
index 80fcef2ed2..a38f045e7f 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
@@ -1,6 +1,8 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -15,9 +17,18 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
public class TestSceneUserPanel : OsuTestScene
{
- private readonly Bindable activity = new Bindable();
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(UserPanel),
+ typeof(UserListPanel),
+ typeof(UserGridPanel),
+ };
- private UserPanel peppy;
+ private readonly Bindable activity = new Bindable();
+ private readonly Bindable status = new Bindable();
+
+ private UserGridPanel peppy;
+ private TestUserListPanel evast;
[Resolved]
private RulesetStore rulesetStore { get; set; }
@@ -25,24 +36,28 @@ namespace osu.Game.Tests.Visual.Online
[SetUp]
public void SetUp() => Schedule(() =>
{
- UserPanel flyte;
+ UserGridPanel flyte;
+
+ activity.Value = null;
+ status.Value = null;
Child = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- AutoSizeAxes = Axes.Both,
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
Spacing = new Vector2(10f),
- Children = new[]
+ Children = new Drawable[]
{
- flyte = new UserPanel(new User
+ flyte = new UserGridPanel(new User
{
Username = @"flyte",
Id = 3103765,
Country = new Country { FlagName = @"JP" },
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
}) { Width = 300 },
- peppy = new UserPanel(new User
+ peppy = new UserGridPanel(new User
{
Username = @"peppy",
Id = 2,
@@ -51,27 +66,40 @@ namespace osu.Game.Tests.Visual.Online
IsSupporter = true,
SupportLevel = 3,
}) { Width = 300 },
+ evast = new TestUserListPanel(new User
+ {
+ Username = @"Evast",
+ Id = 8195163,
+ Country = new Country { FlagName = @"BY" },
+ CoverUrl = @"https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg",
+ IsOnline = false,
+ LastVisit = DateTimeOffset.Now
+ })
},
};
flyte.Status.Value = new UserStatusOnline();
- peppy.Status.Value = null;
+
+ peppy.Status.BindTo(status);
peppy.Activity.BindTo(activity);
+
+ evast.Status.BindTo(status);
+ evast.Activity.BindTo(activity);
});
[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);
+ AddStep("online", () => status.Value = new UserStatusOnline());
+ AddStep("do not disturb", () => status.Value = new UserStatusDoNotDisturb());
+ AddStep("offline", () => status.Value = new UserStatusOffline());
+ AddStep("null status", () => status.Value = null);
}
[Test]
public void TestUserActivity()
{
- AddStep("set online status", () => peppy.Status.Value = new UserStatusOnline());
+ AddStep("set online status", () => status.Value = new UserStatusOnline());
AddStep("idle", () => activity.Value = null);
AddStep("spectating", () => activity.Value = new UserActivity.Spectating());
@@ -84,6 +112,29 @@ namespace osu.Game.Tests.Visual.Online
AddStep("modding", () => activity.Value = new UserActivity.Modding());
}
+ [Test]
+ public void TestUserActivityChange()
+ {
+ AddAssert("visit message is visible", () => evast.LastVisitMessage.IsPresent);
+ AddStep("set online status", () => status.Value = new UserStatusOnline());
+ AddAssert("visit message is not visible", () => !evast.LastVisitMessage.IsPresent);
+ AddStep("set choosing activity", () => activity.Value = new UserActivity.ChoosingBeatmap());
+ AddStep("set offline status", () => status.Value = new UserStatusOffline());
+ AddAssert("visit message is visible", () => evast.LastVisitMessage.IsPresent);
+ AddStep("set online status", () => status.Value = new UserStatusOnline());
+ AddAssert("visit message is not visible", () => !evast.LastVisitMessage.IsPresent);
+ }
+
private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.SoloGame(null, rulesetStore.GetRuleset(rulesetId));
+
+ private class TestUserListPanel : UserListPanel
+ {
+ public TestUserListPanel(User user)
+ : base(user)
+ {
+ }
+
+ public new TextFlowContainer LastVisitMessage => base.LastVisitMessage;
+ }
}
}
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs
new file mode 100644
index 0000000000..0781cba924
--- /dev/null
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs
@@ -0,0 +1,165 @@
+// 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 NUnit.Framework;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Scoring;
+using osu.Game.Screens.Ranking.Expanded.Accuracy;
+using osu.Game.Tests.Beatmaps;
+using osu.Game.Users;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.Ranking
+{
+ public class TestSceneAccuracyCircle : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(AccuracyCircle),
+ typeof(RankBadge),
+ typeof(RankNotch),
+ typeof(RankText),
+ typeof(SmoothCircularProgress)
+ };
+
+ [Test]
+ public void TestLowDRank()
+ {
+ var score = createScore();
+ score.Accuracy = 0.2;
+ score.Rank = ScoreRank.D;
+
+ addCircleStep(score);
+ }
+
+ [Test]
+ public void TestDRank()
+ {
+ var score = createScore();
+ score.Accuracy = 0.5;
+ score.Rank = ScoreRank.D;
+
+ addCircleStep(score);
+ }
+
+ [Test]
+ public void TestCRank()
+ {
+ var score = createScore();
+ score.Accuracy = 0.75;
+ score.Rank = ScoreRank.C;
+
+ addCircleStep(score);
+ }
+
+ [Test]
+ public void TestBRank()
+ {
+ var score = createScore();
+ score.Accuracy = 0.85;
+ score.Rank = ScoreRank.B;
+
+ addCircleStep(score);
+ }
+
+ [Test]
+ public void TestARank()
+ {
+ var score = createScore();
+ score.Accuracy = 0.925;
+ score.Rank = ScoreRank.A;
+
+ addCircleStep(score);
+ }
+
+ [Test]
+ public void TestSRank()
+ {
+ var score = createScore();
+ score.Accuracy = 0.975;
+ score.Rank = ScoreRank.S;
+
+ addCircleStep(score);
+ }
+
+ [Test]
+ public void TestAlmostSSRank()
+ {
+ var score = createScore();
+ score.Accuracy = 0.9999;
+ score.Rank = ScoreRank.S;
+
+ addCircleStep(score);
+ }
+
+ [Test]
+ public void TestSSRank()
+ {
+ var score = createScore();
+ score.Accuracy = 1;
+ score.Rank = ScoreRank.X;
+
+ addCircleStep(score);
+ }
+
+ private void addCircleStep(ScoreInfo score) => AddStep("add panel", () =>
+ {
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(500, 700),
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#555"), Color4Extensions.FromHex("#333"))
+ }
+ }
+ },
+ new AccuracyCircle(score)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(230)
+ }
+ };
+ });
+
+ private ScoreInfo createScore() => new ScoreInfo
+ {
+ User = new User
+ {
+ Id = 2,
+ Username = "peppy",
+ },
+ Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
+ Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
+ TotalScore = 2845370,
+ Accuracy = 0.95,
+ MaxCombo = 999,
+ Rank = ScoreRank.S,
+ Date = DateTimeOffset.Now,
+ Statistics =
+ {
+ { HitResult.Miss, 1 },
+ { HitResult.Meh, 50 },
+ { HitResult.Good, 100 },
+ { HitResult.Great, 300 },
+ }
+ };
+ }
+}
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs
new file mode 100644
index 0000000000..52d8ea0480
--- /dev/null
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs
@@ -0,0 +1,130 @@
+// 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.Bindables;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Testing;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Scoring;
+using osu.Game.Screens.Ranking.Expanded;
+using osu.Game.Screens.Ranking.Expanded.Accuracy;
+using osu.Game.Screens.Ranking.Expanded.Statistics;
+using osu.Game.Tests.Beatmaps;
+using osu.Game.Users;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.Ranking
+{
+ public class TestSceneExpandedPanelMiddleContent : OsuTestScene
+ {
+ [Resolved]
+ private RulesetStore rulesetStore { get; set; }
+
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(ExpandedPanelMiddleContent),
+ typeof(AccuracyCircle),
+ typeof(AccuracyStatistic),
+ typeof(ComboStatistic),
+ typeof(CounterStatistic),
+ typeof(StarRatingDisplay),
+ typeof(StatisticDisplay),
+ typeof(TotalScoreCounter)
+ };
+
+ [Test]
+ public void TestMapWithKnownMapper()
+ {
+ var author = new User { Username = "mapper_name" };
+
+ AddStep("show example score", () => showPanel(createTestBeatmap(author), createTestScore()));
+
+ AddAssert("mapper name present", () => this.ChildrenOfType().Any(spriteText => spriteText.Text == "mapper_name"));
+ }
+
+ [Test]
+ public void TestMapWithUnknownMapper()
+ {
+ AddStep("show example score", () => showPanel(createTestBeatmap(null), createTestScore()));
+
+ AddAssert("mapped by text not present", () =>
+ this.ChildrenOfType().All(spriteText => !containsAny(spriteText.Text, "mapped", "by")));
+ }
+
+ private void showPanel(WorkingBeatmap workingBeatmap, ScoreInfo score)
+ {
+ Child = new ExpandedPanelMiddleContentContainer(workingBeatmap, score);
+ }
+
+ private WorkingBeatmap createTestBeatmap(User author)
+ {
+ var beatmap = new TestBeatmap(rulesetStore.GetRuleset(0));
+ beatmap.Metadata.Author = author;
+
+ return new TestWorkingBeatmap(beatmap);
+ }
+
+ private ScoreInfo createTestScore() => new ScoreInfo
+ {
+ User = new User
+ {
+ Id = 2,
+ Username = "peppy",
+ },
+ Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
+ Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
+ TotalScore = 999999,
+ Accuracy = 0.95,
+ MaxCombo = 999,
+ Rank = ScoreRank.S,
+ Date = DateTimeOffset.Now,
+ Statistics =
+ {
+ { HitResult.Miss, 1 },
+ { HitResult.Meh, 50 },
+ { HitResult.Good, 100 },
+ { HitResult.Great, 300 },
+ }
+ };
+
+ private bool containsAny(string text, params string[] stringsToMatch) => stringsToMatch.Any(text.Contains);
+
+ private class ExpandedPanelMiddleContentContainer : Container
+ {
+ [Cached]
+ private Bindable workingBeatmap { get; set; }
+
+ public ExpandedPanelMiddleContentContainer(WorkingBeatmap beatmap, ScoreInfo score)
+ {
+ workingBeatmap = new Bindable(beatmap);
+
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+ Size = new Vector2(500, 700);
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4Extensions.FromHex("#444"),
+ },
+ new ExpandedPanelMiddleContent(score)
+ };
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelTopContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelTopContent.cs
new file mode 100644
index 0000000000..afaa607099
--- /dev/null
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelTopContent.cs
@@ -0,0 +1,35 @@
+// 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.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Screens.Ranking.Expanded;
+using osu.Game.Users;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.Ranking
+{
+ public class TestSceneExpandedPanelTopContent : OsuTestScene
+ {
+ public TestSceneExpandedPanelTopContent()
+ {
+ Child = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(500, 200),
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4Extensions.FromHex("#444"),
+ },
+ new ExpandedPanelTopContent(new User { Id = 2, Username = "peppy" }),
+ }
+ };
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneResults.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
similarity index 91%
rename from osu.Game.Tests/Visual/Gameplay/TestSceneResults.cs
rename to osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
index 2b7a32ba17..bd5b039bc1 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneResults.cs
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
@@ -10,29 +10,27 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
-using osu.Game.Screens.Ranking.Pages;
+using osu.Game.Tests.Beatmaps;
using osu.Game.Users;
-namespace osu.Game.Tests.Visual.Gameplay
+namespace osu.Game.Tests.Visual.Ranking
{
[TestFixture]
- public class TestSceneResults : ScreenTestScene
+ public class TestSceneResultsScreen : ScreenTestScene
{
private BeatmapManager beatmaps;
public override IReadOnlyList RequiredTypes => new[]
{
- typeof(Results),
- typeof(ResultsPage),
- typeof(ScoreResultsPage),
+ typeof(ResultsScreen),
typeof(RetryButton),
typeof(ReplayDownloadButton),
- typeof(LocalLeaderboardPage),
typeof(TestPlayer)
};
@@ -65,6 +63,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{ HitResult.Meh, 50 },
{ HitResult.Miss, 1 }
},
+ Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
User = new User
{
Username = "peppy",
@@ -119,7 +118,7 @@ namespace osu.Game.Tests.Visual.Gameplay
}
}
- private class TestSoloResults : SoloResults
+ private class TestSoloResults : ResultsScreen
{
public HotkeyRetryOverlay RetryOverlay;
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs
new file mode 100644
index 0000000000..1e55885385
--- /dev/null
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs
@@ -0,0 +1,143 @@
+// 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 NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Scoring;
+using osu.Game.Screens.Ranking;
+using osu.Game.Screens.Ranking.Expanded;
+using osu.Game.Tests.Beatmaps;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual.Ranking
+{
+ public class TestSceneScorePanel : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(ScorePanel),
+ typeof(PanelState),
+ typeof(ExpandedPanelMiddleContent),
+ typeof(ExpandedPanelTopContent),
+ };
+
+ [Test]
+ public void TestDRank()
+ {
+ var score = createScore();
+ score.Accuracy = 0.5;
+ score.Rank = ScoreRank.D;
+
+ addPanelStep(score);
+ }
+
+ [Test]
+ public void TestCRank()
+ {
+ var score = createScore();
+ score.Accuracy = 0.75;
+ score.Rank = ScoreRank.C;
+
+ addPanelStep(score);
+ }
+
+ [Test]
+ public void TestBRank()
+ {
+ var score = createScore();
+ score.Accuracy = 0.85;
+ score.Rank = ScoreRank.B;
+
+ addPanelStep(score);
+ }
+
+ [Test]
+ public void TestARank()
+ {
+ var score = createScore();
+ score.Accuracy = 0.925;
+ score.Rank = ScoreRank.A;
+
+ addPanelStep(score);
+ }
+
+ [Test]
+ public void TestSRank()
+ {
+ var score = createScore();
+ score.Accuracy = 0.975;
+ score.Rank = ScoreRank.S;
+
+ addPanelStep(score);
+ }
+
+ [Test]
+ public void TestAlmostSSRank()
+ {
+ var score = createScore();
+ score.Accuracy = 0.9999;
+ score.Rank = ScoreRank.S;
+
+ addPanelStep(score);
+ }
+
+ [Test]
+ public void TestSSRank()
+ {
+ var score = createScore();
+ score.Accuracy = 1;
+ score.Rank = ScoreRank.X;
+
+ addPanelStep(score);
+ }
+
+ [Test]
+ public void TestAllHitResults()
+ {
+ var score = createScore();
+ score.Statistics[HitResult.Perfect] = 350;
+ score.Statistics[HitResult.Ok] = 200;
+
+ addPanelStep(score);
+ }
+
+ private void addPanelStep(ScoreInfo score) => AddStep("add panel", () =>
+ {
+ Child = new ScorePanel(score)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ State = PanelState.Expanded
+ };
+ });
+
+ private ScoreInfo createScore() => new ScoreInfo
+ {
+ User = new User
+ {
+ Id = 2,
+ Username = "peppy",
+ },
+ Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
+ Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
+ TotalScore = 2845370,
+ Accuracy = 0.95,
+ MaxCombo = 999,
+ Rank = ScoreRank.S,
+ Date = DateTimeOffset.Now,
+ Statistics =
+ {
+ { HitResult.Miss, 1 },
+ { HitResult.Meh, 50 },
+ { HitResult.Good, 100 },
+ { HitResult.Great, 300 },
+ }
+ };
+ }
+}
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs
new file mode 100644
index 0000000000..d12f32e470
--- /dev/null
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs
@@ -0,0 +1,32 @@
+// 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.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
+using osu.Game.Screens.Ranking.Expanded;
+
+namespace osu.Game.Tests.Visual.Ranking
+{
+ public class TestSceneStarRatingDisplay : OsuTestScene
+ {
+ public TestSceneStarRatingDisplay()
+ {
+ Child = new FillFlowContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Children = new Drawable[]
+ {
+ new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 1.23 }),
+ new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 2.34 }),
+ new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 3.45 }),
+ new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 4.56 }),
+ new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 5.67 }),
+ new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 6.78 }),
+ new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 10.11 }),
+ }
+ };
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs
index 80e03d82e2..0cc37bbd57 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs
@@ -227,6 +227,34 @@ namespace osu.Game.Tests.Visual.SongSelect
waitForSelection(set_count);
}
+ [Test]
+ public void TestSelectionEnteringFromEmptyRuleset()
+ {
+ var sets = new List();
+
+ AddStep("Create beatmaps for taiko only", () =>
+ {
+ sets.Clear();
+
+ var rulesetBeatmapSet = createTestBeatmapSet(1);
+ var taikoRuleset = rulesets.AvailableRulesets.ElementAt(1);
+ rulesetBeatmapSet.Beatmaps.ForEach(b =>
+ {
+ b.Ruleset = taikoRuleset;
+ b.RulesetID = 1;
+ });
+
+ sets.Add(rulesetBeatmapSet);
+ });
+
+ loadBeatmaps(sets, () => new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) });
+
+ AddStep("Set non-empty mode filter", () =>
+ carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(1) }, false));
+
+ AddAssert("Something is selected", () => carousel.SelectedBeatmap != null);
+ }
+
///
/// Test sorting
///
@@ -399,27 +427,32 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("filter to ruleset 0", () =>
carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) }, false));
AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testMixed.Beatmaps[1], false));
- AddAssert("unfiltered beatmap selected", () => carousel.SelectedBeatmap.Equals(testMixed.Beatmaps[0]));
+ AddAssert("unfiltered beatmap not selected", () => carousel.SelectedBeatmap.RulesetID == 0);
AddStep("remove mixed set", () =>
{
carousel.RemoveBeatmapSet(testMixed);
testMixed = null;
});
- var testSingle = createTestBeatmapSet(set_count + 2);
- testSingle.Beatmaps.ForEach(b =>
+ BeatmapSetInfo testSingle = null;
+ AddStep("add single ruleset beatmapset", () =>
{
- b.Ruleset = rulesets.AvailableRulesets.ElementAt(1);
- b.RulesetID = b.Ruleset.ID ?? 1;
+ testSingle = createTestBeatmapSet(set_count + 2);
+ testSingle.Beatmaps.ForEach(b =>
+ {
+ b.Ruleset = rulesets.AvailableRulesets.ElementAt(1);
+ b.RulesetID = b.Ruleset.ID ?? 1;
+ });
+
+ carousel.UpdateBeatmapSet(testSingle);
});
- AddStep("add single ruleset beatmapset", () => carousel.UpdateBeatmapSet(testSingle));
AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testSingle.Beatmaps[0], false));
checkNoSelection();
AddStep("remove single ruleset set", () => carousel.RemoveBeatmapSet(testSingle));
}
[Test]
- public void TestCarouselRootIsRandom()
+ public void TestCarouselRemembersSelection()
{
List manySets = new List();
@@ -429,12 +462,74 @@ namespace osu.Game.Tests.Visual.SongSelect
loadBeatmaps(manySets);
advanceSelection(direction: 1, diff: false);
- checkNonmatchingFilter();
- checkNonmatchingFilter();
- checkNonmatchingFilter();
- checkNonmatchingFilter();
- checkNonmatchingFilter();
- AddAssert("Selection was random", () => eagerSelectedIDs.Count > 1);
+
+ for (int i = 0; i < 5; i++)
+ {
+ AddStep("Toggle non-matching filter", () =>
+ {
+ carousel.Filter(new FilterCriteria { SearchText = Guid.NewGuid().ToString() }, false);
+ });
+
+ AddStep("Restore no filter", () =>
+ {
+ carousel.Filter(new FilterCriteria(), false);
+ eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID);
+ });
+ }
+
+ // always returns to same selection as long as it's available.
+ AddAssert("Selection was remembered", () => eagerSelectedIDs.Count == 1);
+ }
+
+ [Test]
+ public void TestRandomFallbackOnNonMatchingPrevious()
+ {
+ List manySets = new List();
+
+ AddStep("populate maps", () =>
+ {
+ for (int i = 0; i < 10; i++)
+ {
+ var set = createTestBeatmapSet(i);
+
+ foreach (var b in set.Beatmaps)
+ {
+ // all taiko except for first
+ int ruleset = i > 0 ? 1 : 0;
+
+ b.Ruleset = rulesets.GetRuleset(ruleset);
+ b.RulesetID = ruleset;
+ }
+
+ manySets.Add(set);
+ }
+ });
+
+ loadBeatmaps(manySets);
+
+ for (int i = 0; i < 10; i++)
+ {
+ AddStep("Reset filter", () => carousel.Filter(new FilterCriteria(), false));
+
+ AddStep("select first beatmap", () => carousel.SelectBeatmap(manySets.First().Beatmaps.First()));
+
+ AddStep("Toggle non-matching filter", () =>
+ {
+ carousel.Filter(new FilterCriteria { SearchText = Guid.NewGuid().ToString() }, false);
+ });
+
+ AddAssert("selection lost", () => carousel.SelectedBeatmap == null);
+
+ AddStep("Restore different ruleset filter", () =>
+ {
+ carousel.Filter(new FilterCriteria { Ruleset = rulesets.GetRuleset(1) }, false);
+ eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID);
+ });
+
+ AddAssert("selection changed", () => carousel.SelectedBeatmap != manySets.First().Beatmaps.First());
+ }
+
+ AddAssert("Selection was random", () => eagerSelectedIDs.Count > 2);
}
[Test]
@@ -484,7 +579,7 @@ namespace osu.Game.Tests.Visual.SongSelect
checkVisibleItemCount(true, 15);
}
- private void loadBeatmaps(List beatmapSets = null)
+ private void loadBeatmaps(List beatmapSets = null, Func initialCriteria = null)
{
createCarousel();
@@ -499,7 +594,7 @@ namespace osu.Game.Tests.Visual.SongSelect
bool changed = false;
AddStep($"Load {(beatmapSets.Count > 0 ? beatmapSets.Count.ToString() : "some")} beatmaps", () =>
{
- carousel.Filter(new FilterCriteria());
+ carousel.Filter(initialCriteria?.Invoke() ?? new FilterCriteria());
carousel.BeatmapSetsChanged = () => changed = true;
carousel.BeatmapSets = beatmapSets;
});
@@ -593,16 +688,6 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("Selection is visible", selectedBeatmapVisible);
}
- private void checkNonmatchingFilter()
- {
- AddStep("Toggle non-matching filter", () =>
- {
- carousel.Filter(new FilterCriteria { SearchText = "Dingo" }, false);
- carousel.Filter(new FilterCriteria(), false);
- eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID);
- });
- }
-
private BeatmapSetInfo createTestBeatmapSet(int id)
{
return new BeatmapSetInfo
diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
index 105d96cdfe..4405c75744 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
@@ -283,7 +283,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("import multi-ruleset map", () =>
{
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
- manager.Import(createTestBeatmapSet(0, usableRulesets)).Wait();
+ manager.Import(createTestBeatmapSet(usableRulesets)).Wait();
});
}
else
@@ -436,6 +436,9 @@ namespace osu.Game.Tests.Visual.SongSelect
changeRuleset(0);
+ // used for filter check below
+ AddStep("allow convert display", () => config.Set(OsuSetting.ShowConvertedBeatmaps, true));
+
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null);
AddStep("set filter text", () => songSelect.FilterControl.ChildrenOfType().First().Text = "nonono");
@@ -446,16 +449,28 @@ namespace osu.Game.Tests.Visual.SongSelect
BeatmapInfo target = null;
+ int targetRuleset = differentRuleset ? 1 : 0;
+
AddStep("select beatmap externally", () =>
{
- target = manager.GetAllUsableBeatmapSets().Where(b => b.Beatmaps.Any(bi => bi.RulesetID == (differentRuleset ? 1 : 0)))
- .ElementAt(5).Beatmaps.First();
+ target = manager.GetAllUsableBeatmapSets()
+ .Where(b => b.Beatmaps.Any(bi => bi.RulesetID == targetRuleset))
+ .ElementAt(5).Beatmaps.First(bi => bi.RulesetID == targetRuleset);
Beatmap.Value = manager.GetWorkingBeatmap(target);
});
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null);
+ AddAssert("selected only shows expected ruleset (plus converts)", () =>
+ {
+ var selectedPanel = songSelect.Carousel.ChildrenOfType().First(s => s.Item.State.Value == CarouselItemState.Selected);
+
+ // special case for converts checked here.
+ return selectedPanel.ChildrenOfType().All(i =>
+ i.IsFiltered || i.Item.Beatmap.Ruleset.ID == targetRuleset || i.Item.Beatmap.Ruleset.ID == 0);
+ });
+
AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmap?.OnlineBeatmapID == target.OnlineBeatmapID);
AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID);
@@ -557,6 +572,7 @@ namespace osu.Game.Tests.Visual.SongSelect
difficultyIcon = set.ChildrenOfType()
.First(icon => getDifficultyIconIndex(set, icon) != getCurrentBeatmapIndex());
});
+
AddStep("Click on a difficulty", () =>
{
InputManager.MoveMouseTo(difficultyIcon);
@@ -564,6 +580,7 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.PressButton(MouseButton.Left);
InputManager.ReleaseButton(MouseButton.Left);
});
+
AddAssert("Selected beatmap correct", () => getCurrentBeatmapIndex() == getDifficultyIconIndex(set, difficultyIcon));
double? maxBPM = null;
@@ -576,16 +593,16 @@ namespace osu.Game.Tests.Visual.SongSelect
}
}));
+ BeatmapInfo filteredBeatmap = null;
DrawableCarouselBeatmapSet.FilterableDifficultyIcon filteredIcon = null;
+
AddStep("Get filtered icon", () =>
{
- var filteredBeatmap = songSelect.Carousel.SelectedBeatmapSet.Beatmaps.Find(b => b.BPM < maxBPM);
+ filteredBeatmap = songSelect.Carousel.SelectedBeatmapSet.Beatmaps.First(b => b.BPM < maxBPM);
int filteredBeatmapIndex = getBeatmapIndex(filteredBeatmap.BeatmapSet, filteredBeatmap);
filteredIcon = set.ChildrenOfType().ElementAt(filteredBeatmapIndex);
});
- int? previousID = null;
- AddStep("Store current ID", () => previousID = songSelect.Carousel.SelectedBeatmap.ID);
AddStep("Click on a filtered difficulty", () =>
{
InputManager.MoveMouseTo(filteredIcon);
@@ -593,7 +610,101 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.PressButton(MouseButton.Left);
InputManager.ReleaseButton(MouseButton.Left);
});
- AddAssert("Selected beatmap has not changed", () => songSelect.Carousel.SelectedBeatmap.ID == previousID);
+
+ AddAssert("Selected beatmap correct", () => songSelect.Carousel.SelectedBeatmap == filteredBeatmap);
+ }
+
+ [Test]
+ public void TestDifficultyIconSelectingForDifferentRuleset()
+ {
+ changeRuleset(0);
+
+ createSongSelect();
+
+ AddStep("import multi-ruleset map", () =>
+ {
+ var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
+ manager.Import(createTestBeatmapSet(usableRulesets)).Wait();
+ });
+
+ DrawableCarouselBeatmapSet set = null;
+ AddUntilStep("Find the DrawableCarouselBeatmapSet", () =>
+ {
+ set = songSelect.Carousel.ChildrenOfType().FirstOrDefault();
+ return set != null;
+ });
+
+ DrawableCarouselBeatmapSet.FilterableDifficultyIcon difficultyIcon = null;
+ AddStep("Find an icon for different ruleset", () =>
+ {
+ difficultyIcon = set.ChildrenOfType()
+ .First(icon => icon.Item.Beatmap.Ruleset.ID == 3);
+ });
+
+ AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0);
+
+ int previousSetID = 0;
+
+ AddStep("record set ID", () => previousSetID = Beatmap.Value.BeatmapSetInfo.ID);
+
+ AddStep("Click on a difficulty", () =>
+ {
+ InputManager.MoveMouseTo(difficultyIcon);
+
+ InputManager.PressButton(MouseButton.Left);
+ InputManager.ReleaseButton(MouseButton.Left);
+ });
+
+ AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3);
+
+ AddAssert("Selected beatmap still same set", () => songSelect.Carousel.SelectedBeatmap.BeatmapSet.ID == previousSetID);
+ AddAssert("Selected beatmap is mania", () => Beatmap.Value.BeatmapInfo.Ruleset.ID == 3);
+ }
+
+ [Test]
+ public void TestGroupedDifficultyIconSelecting()
+ {
+ changeRuleset(0);
+
+ createSongSelect();
+
+ BeatmapSetInfo imported = null;
+
+ AddStep("import huge difficulty count map", () =>
+ {
+ var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
+ imported = manager.Import(createTestBeatmapSet(usableRulesets, 50)).Result;
+ });
+
+ AddStep("select the first beatmap of import", () => Beatmap.Value = manager.GetWorkingBeatmap(imported.Beatmaps.First()));
+
+ DrawableCarouselBeatmapSet set = null;
+ AddUntilStep("Find the DrawableCarouselBeatmapSet", () =>
+ {
+ set = songSelect.Carousel.ChildrenOfType().FirstOrDefault();
+ return set != null;
+ });
+
+ DrawableCarouselBeatmapSet.FilterableGroupedDifficultyIcon groupIcon = null;
+ AddStep("Find group icon for different ruleset", () =>
+ {
+ groupIcon = set.ChildrenOfType()
+ .First(icon => icon.Items.First().Beatmap.Ruleset.ID == 3);
+ });
+
+ AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0);
+
+ AddStep("Click on group", () =>
+ {
+ InputManager.MoveMouseTo(groupIcon);
+
+ InputManager.PressButton(MouseButton.Left);
+ InputManager.ReleaseButton(MouseButton.Left);
+ });
+
+ AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3);
+
+ AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo == groupIcon.Items.First().Beatmap);
}
private int getBeatmapIndex(BeatmapSetInfo set, BeatmapInfo info) => set.Beatmaps.FindIndex(b => b == info);
@@ -607,7 +718,7 @@ namespace osu.Game.Tests.Visual.SongSelect
private void addRulesetImportStep(int id) => AddStep($"import test map for ruleset {id}", () => importForRuleset(id));
- private void importForRuleset(int id) => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())).Wait();
+ private void importForRuleset(int id) => manager.Import(createTestBeatmapSet(rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())).Wait();
private static int importId;
@@ -633,20 +744,22 @@ namespace osu.Game.Tests.Visual.SongSelect
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
for (int i = 0; i < 100; i += 10)
- manager.Import(createTestBeatmapSet(i, usableRulesets)).Wait();
+ manager.Import(createTestBeatmapSet(usableRulesets)).Wait();
});
}
- private BeatmapSetInfo createTestBeatmapSet(int setId, RulesetInfo[] rulesets)
+ private BeatmapSetInfo createTestBeatmapSet(RulesetInfo[] rulesets, int countPerRuleset = 6)
{
int j = 0;
RulesetInfo getRuleset() => rulesets[j++ % rulesets.Length];
+ int setId = getImportId();
+
var beatmaps = new List();
- for (int i = 0; i < 6; i++)
+ for (int i = 0; i < countPerRuleset; i++)
{
- int beatmapId = setId * 10 + i;
+ int beatmapId = setId * 1000 + i;
int length = RNG.Next(30000, 200000);
double bpm = RNG.NextSingle(80, 200);
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneCommentEditor.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneCommentEditor.cs
index 7b0b644dab..cef04a4c18 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneCommentEditor.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneCommentEditor.cs
@@ -15,7 +15,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
- public class TestSceneCommentEditor : ManualInputManagerTestScene
+ public class TestSceneCommentEditor : OsuManualInputManagerTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs
index d1dde4664a..5b74852259 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs
@@ -17,7 +17,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
[TestFixture]
- public class TestSceneCursors : ManualInputManagerTestScene
+ public class TestSceneCursors : OsuManualInputManagerTestScene
{
private readonly MenuCursorContainer menuCursorContainer;
private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6];
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs
index 1e5e26e4c5..a812b4dc79 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs
@@ -27,7 +27,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
- public class TestSceneDeleteLocalScore : ManualInputManagerTestScene
+ public class TestSceneDeleteLocalScore : OsuManualInputManagerTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFriendsOnlineStatusControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFriendsOnlineStatusControl.cs
index 0d841dfef1..f6dcf78d55 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneFriendsOnlineStatusControl.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFriendsOnlineStatusControl.cs
@@ -8,7 +8,7 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Overlays;
-using osu.Game.Overlays.Home.Friends;
+using osu.Game.Overlays.Dashboard.Friends;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.UserInterface
@@ -17,20 +17,20 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public override IReadOnlyList RequiredTypes => new[]
{
- typeof(FriendsOnlineStatusControl),
+ typeof(FriendOnlineStreamControl),
typeof(FriendsOnlineStatusItem),
typeof(OverlayStreamControl<>),
typeof(OverlayStreamItem<>),
- typeof(FriendsBundle)
+ typeof(FriendStream)
};
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
- private FriendsOnlineStatusControl control;
+ private FriendOnlineStreamControl control;
[SetUp]
- public void SetUp() => Schedule(() => Child = control = new FriendsOnlineStatusControl
+ public void SetUp() => Schedule(() => Child = control = new FriendOnlineStreamControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -55,9 +55,9 @@ namespace osu.Game.Tests.Visual.UserInterface
}
}));
- 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);
+ AddAssert("3 users", () => control.Items.FirstOrDefault(item => item.Status == OnlineStatus.All)?.Count == 3);
+ AddAssert("1 online user", () => control.Items.FirstOrDefault(item => item.Status == OnlineStatus.Online)?.Count == 1);
+ AddAssert("2 offline users", () => control.Items.FirstOrDefault(item => item.Status == OnlineStatus.Offline)?.Count == 2);
}
}
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs
index dbef7d1686..396bec51b6 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuHoverContainer.cs
@@ -12,7 +12,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
[TestFixture]
- public class TestSceneOsuHoverContainer : ManualInputManagerTestScene
+ public class TestSceneOsuHoverContainer : OsuManualInputManagerTestScene
{
private OsuHoverTestContainer hoverContainer;
private Box colourContainer;
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs
index 2ada5b927b..85fea73bf5 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs
@@ -12,7 +12,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
- public class TestSceneStatefulMenuItem : ManualInputManagerTestScene
+ public class TestSceneStatefulMenuItem : OsuManualInputManagerTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs
index 4a104b4a41..37fab75aee 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs
@@ -9,7 +9,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
- public class TestSceneSwitchButton : ManualInputManagerTestScene
+ public class TestSceneSwitchButton : OsuManualInputManagerTestScene
{
private SwitchButton switchButton;
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUserListToolbar.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUserListToolbar.cs
index 02b8839922..1546972580 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneUserListToolbar.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUserListToolbar.cs
@@ -8,7 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
-using osu.Game.Overlays.Home.Friends;
+using osu.Game.Overlays.Dashboard.Friends;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs
index 53ce5def32..90c91eb007 100644
--- a/osu.Game.Tests/WaveformTestBeatmap.cs
+++ b/osu.Game.Tests/WaveformTestBeatmap.cs
@@ -6,7 +6,6 @@ using System.Linq;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Graphics.Video;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO;
@@ -51,8 +50,6 @@ namespace osu.Game.Tests
protected override Texture GetBackground() => null;
- protected override VideoSprite GetVideo() => null;
-
protected override Waveform GetWaveform() => new Waveform(trackStore.GetStream(firstAudioFile));
protected override Track GetTrack() => trackStore.Get(firstAudioFile);
diff --git a/osu.Game.Tournament.Tests/Components/TestSceneRoundDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneRoundDisplay.cs
new file mode 100644
index 0000000000..6f71627ce4
--- /dev/null
+++ b/osu.Game.Tournament.Tests/Components/TestSceneRoundDisplay.cs
@@ -0,0 +1,40 @@
+// 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.Game.Tournament.Components;
+using osu.Game.Tournament.Models;
+
+namespace osu.Game.Tournament.Tests.Components
+{
+ public class TestSceneRoundDisplay : TournamentTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DrawableTournamentHeaderText),
+ typeof(DrawableTournamentHeaderLogo),
+ };
+
+ public TestSceneRoundDisplay()
+ {
+ Children = new Drawable[]
+ {
+ new RoundDisplay(new TournamentMatch
+ {
+ Round =
+ {
+ Value = new TournamentRound
+ {
+ Name = { Value = "Test Round" }
+ }
+ }
+ })
+ {
+ Margin = new MarginPadding(20)
+ }
+ };
+ }
+ }
+}
diff --git a/osu.Game.Tournament.Tests/LadderTestScene.cs b/osu.Game.Tournament.Tests/LadderTestScene.cs
index dae0721023..b962d035ab 100644
--- a/osu.Game.Tournament.Tests/LadderTestScene.cs
+++ b/osu.Game.Tournament.Tests/LadderTestScene.cs
@@ -1,16 +1,147 @@
// Copyright (c) ppy Pty Ltd . 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.Allocation;
+using osu.Framework.Utils;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets;
using osu.Game.Tournament.Models;
+using osu.Game.Users;
namespace osu.Game.Tournament.Tests
{
[TestFixture]
public abstract class LadderTestScene : TournamentTestScene
{
+ [Cached]
+ protected LadderInfo Ladder { get; private set; } = new LadderInfo();
+
[Resolved]
- protected LadderInfo Ladder { get; private set; }
+ private RulesetStore rulesetStore { get; set; }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ if (Ladder.Ruleset.Value == null)
+ Ladder.Ruleset.Value = rulesetStore.AvailableRulesets.First();
+
+ Ruleset.BindTo(Ladder.Ruleset);
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ TournamentMatch match = CreateSampleMatch();
+
+ Ladder.Rounds.Add(match.Round.Value);
+ Ladder.Matches.Add(match);
+ Ladder.Teams.Add(match.Team1.Value);
+ Ladder.Teams.Add(match.Team2.Value);
+
+ Ladder.CurrentMatch.Value = match;
+ }
+
+ public static TournamentMatch CreateSampleMatch() => 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 = CreateSampleBeatmapInfo(),
+ Score = 12345672,
+ Seed = { Value = 24 },
+ },
+ new SeedingBeatmap
+ {
+ BeatmapInfo = CreateSampleBeatmapInfo(),
+ Score = 1234567,
+ Seed = { Value = 12 },
+ },
+ new SeedingBeatmap
+ {
+ BeatmapInfo = CreateSampleBeatmapInfo(),
+ Score = 1234567,
+ Seed = { Value = 16 },
+ }
+ }
+ },
+ new SeedingResult
+ {
+ Mod = { Value = "DT" },
+ Seed = { Value = 5 },
+ Beatmaps =
+ {
+ new SeedingBeatmap
+ {
+ BeatmapInfo = CreateSampleBeatmapInfo(),
+ Score = 234567,
+ Seed = { Value = 3 },
+ },
+ new SeedingBeatmap
+ {
+ BeatmapInfo = CreateSampleBeatmapInfo(),
+ Score = 234567,
+ Seed = { Value = 6 },
+ },
+ new SeedingBeatmap
+ {
+ BeatmapInfo = CreateSampleBeatmapInfo(),
+ 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" } }
+ }
+ };
+
+ public static BeatmapInfo CreateSampleBeatmapInfo() =>
+ new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist", ID = RNG.Next(0, 1000000) } };
}
}
diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs
index a7011c6d3c..a4538be384 100644
--- a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs
+++ b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs
@@ -1,24 +1,140 @@
// 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.Containers;
+using osu.Framework.Testing;
+using osu.Game.Tournament.Components;
+using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.MapPool;
namespace osu.Game.Tournament.Tests.Screens
{
public class TestSceneMapPoolScreen : LadderTestScene
{
- public override IReadOnlyList RequiredTypes => new[]
- {
- typeof(MapPoolScreen)
- };
+ private MapPoolScreen screen;
[BackgroundDependencyLoader]
private void load()
{
- Add(new MapPoolScreen { Width = 0.7f });
+ Add(screen = new MapPoolScreen { Width = 0.7f });
+ }
+
+ [Test]
+ public void TestFewMaps()
+ {
+ AddStep("load few maps", () =>
+ {
+ Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
+
+ for (int i = 0; i < 8; i++)
+ addBeatmap();
+ });
+
+ AddStep("reset match", () =>
+ {
+ Ladder.CurrentMatch.Value = new TournamentMatch();
+ Ladder.CurrentMatch.Value = Ladder.Matches.First();
+ });
+
+ assertTwoWide();
+ }
+
+ [Test]
+ public void TestJustEnoughMaps()
+ {
+ AddStep("load just enough maps", () =>
+ {
+ Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
+
+ for (int i = 0; i < 18; i++)
+ addBeatmap();
+ });
+
+ AddStep("reset match", () =>
+ {
+ Ladder.CurrentMatch.Value = new TournamentMatch();
+ Ladder.CurrentMatch.Value = Ladder.Matches.First();
+ });
+
+ assertTwoWide();
+ }
+
+ [Test]
+ public void TestManyMaps()
+ {
+ AddStep("load many maps", () =>
+ {
+ Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
+
+ for (int i = 0; i < 19; i++)
+ addBeatmap();
+ });
+
+ AddStep("reset match", () =>
+ {
+ Ladder.CurrentMatch.Value = new TournamentMatch();
+ Ladder.CurrentMatch.Value = Ladder.Matches.First();
+ });
+
+ assertThreeWide();
+ }
+
+ [Test]
+ public void TestJustEnoughMods()
+ {
+ AddStep("load many maps", () =>
+ {
+ Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
+
+ for (int i = 0; i < 11; i++)
+ addBeatmap(i > 4 ? $"M{i}" : "NM");
+ });
+
+ AddStep("reset match", () =>
+ {
+ Ladder.CurrentMatch.Value = new TournamentMatch();
+ Ladder.CurrentMatch.Value = Ladder.Matches.First();
+ });
+
+ assertTwoWide();
+ }
+
+ private void assertTwoWide() =>
+ AddAssert("ensure layout width is 2", () => screen.ChildrenOfType>>().First().Padding.Left > 0);
+
+ private void assertThreeWide() =>
+ AddAssert("ensure layout width is 3", () => screen.ChildrenOfType>>().First().Padding.Left == 0);
+
+ [Test]
+ public void TestManyMods()
+ {
+ AddStep("load many maps", () =>
+ {
+ Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
+
+ for (int i = 0; i < 12; i++)
+ addBeatmap(i > 4 ? $"M{i}" : "NM");
+ });
+
+ AddStep("reset match", () =>
+ {
+ Ladder.CurrentMatch.Value = new TournamentMatch();
+ Ladder.CurrentMatch.Value = Ladder.Matches.First();
+ });
+
+ assertThreeWide();
+ }
+
+ private void addBeatmap(string mods = "nm")
+ {
+ Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Add(new RoundBeatmap
+ {
+ BeatmapInfo = CreateSampleBeatmapInfo(),
+ Mods = mods
+ });
}
}
}
diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneSeedingEditorScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneSeedingEditorScreen.cs
index 014cd4663b..17cccd34b6 100644
--- a/osu.Game.Tournament.Tests/Screens/TestSceneSeedingEditorScreen.cs
+++ b/osu.Game.Tournament.Tests/Screens/TestSceneSeedingEditorScreen.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Tournament.Tests.Screens
public TestSceneSeedingEditorScreen()
{
- var match = TestSceneSeedingScreen.CreateSampleSeededMatch();
+ var match = CreateSampleMatch();
Add(new SeedingEditorScreen(match.Team1.Value)
{
diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneSeedingScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneSeedingScreen.cs
index 335a6c80a1..4269f8f56a 100644
--- a/osu.Game.Tournament.Tests/Screens/TestSceneSeedingScreen.cs
+++ b/osu.Game.Tournament.Tests/Screens/TestSceneSeedingScreen.cs
@@ -3,10 +3,8 @@
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
{
@@ -18,110 +16,11 @@ namespace osu.Game.Tournament.Tests.Screens
[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" } }
- }
- };
}
}
diff --git a/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs b/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs
index bda696ba00..99d914fed4 100644
--- a/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs
+++ b/osu.Game.Tournament/Components/DrawableTournamentHeaderText.cs
@@ -11,9 +11,13 @@ namespace osu.Game.Tournament.Components
{
public class DrawableTournamentHeaderText : CompositeDrawable
{
- public DrawableTournamentHeaderText()
+ public DrawableTournamentHeaderText(bool center = true)
{
- InternalChild = new TextSprite();
+ InternalChild = new TextSprite
+ {
+ Anchor = center ? Anchor.Centre : Anchor.TopLeft,
+ Origin = center ? Anchor.Centre : Anchor.TopLeft,
+ };
Height = 22;
RelativeSizeAxes = Axes.X;
@@ -27,9 +31,6 @@ namespace osu.Game.Tournament.Components
RelativeSizeAxes = Axes.Both;
FillMode = FillMode.Fit;
- Anchor = Anchor.Centre;
- Origin = Anchor.Centre;
-
Texture = textures.Get("header-text");
}
}
diff --git a/osu.Game.Tournament/Components/RoundDisplay.cs b/osu.Game.Tournament/Components/RoundDisplay.cs
index bebede6782..c0002e6804 100644
--- a/osu.Game.Tournament/Components/RoundDisplay.cs
+++ b/osu.Game.Tournament/Components/RoundDisplay.cs
@@ -12,19 +12,27 @@ namespace osu.Game.Tournament.Components
{
public RoundDisplay(TournamentMatch match)
{
- AutoSizeAxes = Axes.Both;
+ AutoSizeAxes = Axes.Y;
+ RelativeSizeAxes = Axes.X;
InternalChildren = new Drawable[]
{
new FillFlowContainer
{
- AutoSizeAxes = Axes.Both,
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- new DrawableTournamentHeaderText(),
+ new DrawableTournamentHeaderText(false)
+ {
+ Anchor = Anchor.TopLeft,
+ Origin = Anchor.TopLeft,
+ },
new TournamentSpriteText
{
+ Anchor = Anchor.TopLeft,
+ Origin = Anchor.TopLeft,
Text = match.Round.Value?.Name.Value ?? "Unknown Round",
Font = OsuFont.Torus.With(size: 26, weight: FontWeight.SemiBold)
},
diff --git a/osu.Game.Tournament/Components/TournamentMatchChatDisplay.cs b/osu.Game.Tournament/Components/TournamentMatchChatDisplay.cs
index 2a183d0d45..fe22d1e76d 100644
--- a/osu.Game.Tournament/Components/TournamentMatchChatDisplay.cs
+++ b/osu.Game.Tournament/Components/TournamentMatchChatDisplay.cs
@@ -70,6 +70,17 @@ namespace osu.Game.Tournament.Components
protected override ChatLine CreateMessage(Message message) => new MatchMessage(message);
+ protected override StandAloneDrawableChannel CreateDrawableChannel(Channel channel) => new MatchChannel(channel);
+
+ public class MatchChannel : StandAloneDrawableChannel
+ {
+ public MatchChannel(Channel channel)
+ : base(channel)
+ {
+ ScrollbarVisible = false;
+ }
+ }
+
protected class MatchMessage : StandAloneMessage
{
public MatchMessage(Message message)
diff --git a/osu.Game.Tournament/Models/LadderInfo.cs b/osu.Game.Tournament/Models/LadderInfo.cs
index 5db0b01547..c2e6da9ca5 100644
--- a/osu.Game.Tournament/Models/LadderInfo.cs
+++ b/osu.Game.Tournament/Models/LadderInfo.cs
@@ -24,7 +24,13 @@ namespace osu.Game.Tournament.Models
// only used for serialisation
public List Progressions = new List();
- [JsonIgnore]
+ [JsonIgnore] // updated manually in TournamentGameBase
public Bindable CurrentMatch = new Bindable();
+
+ public Bindable ChromaKeyWidth = new BindableInt(1024)
+ {
+ MinValue = 640,
+ MaxValue = 1366,
+ };
}
}
diff --git a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs
index 7468c9484d..81487f1bcf 100644
--- a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs
+++ b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs
@@ -296,7 +296,7 @@ namespace osu.Game.Tournament.Screens.Editors
private void updatePanel()
{
- drawableContainer.Child = new UserPanel(user) { Width = 300 };
+ drawableContainer.Child = new UserGridPanel(user) { Width = 300 };
}
}
}
diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs
index 462015f004..3e60a03f92 100644
--- a/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs
+++ b/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs
@@ -35,7 +35,9 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
private void load(LadderInfo ladder)
{
currentMatch.BindTo(ladder.CurrentMatch);
- currentMatch.BindValueChanged(matchChanged, true);
+ currentMatch.BindValueChanged(matchChanged);
+
+ updateMatch();
}
private void matchChanged(ValueChangedEvent match)
@@ -43,10 +45,19 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
currentTeamScore.UnbindBindings();
currentTeam.UnbindBindings();
- if (match.NewValue != null)
+ Scheduler.AddOnce(updateMatch);
+ }
+
+ private void updateMatch()
+ {
+ var match = currentMatch.Value;
+
+ if (match != null)
{
- currentTeamScore.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1Score : match.NewValue.Team2Score);
- currentTeam.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1 : match.NewValue.Team2);
+ match.StartMatch();
+
+ currentTeamScore.BindTo(teamColour == TeamColour.Red ? match.Team1Score : match.Team2Score);
+ currentTeam.BindTo(teamColour == TeamColour.Red ? match.Team1 : match.Team2);
}
// team may change to same team, which means score is not in a good state.
diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs
index 8920990d1b..64a5cd6dec 100644
--- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs
+++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs
@@ -9,6 +9,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Platform;
using osu.Framework.Threading;
using osu.Game.Graphics.UserInterface;
+using osu.Game.Overlays.Settings;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.IPC;
using osu.Game.Tournament.Models;
@@ -35,6 +36,8 @@ namespace osu.Game.Tournament.Screens.Gameplay
[Resolved]
private TournamentMatchChatDisplay chat { get; set; }
+ private Box chroma;
+
[BackgroundDependencyLoader]
private void load(LadderInfo ladder, MatchIPCInfo ipc, Storage storage)
{
@@ -60,11 +63,10 @@ namespace osu.Game.Tournament.Screens.Gameplay
Origin = Anchor.TopCentre,
Children = new Drawable[]
{
- new Box
+ chroma = new Box
{
// chroma key area for stable gameplay
Name = "chroma",
- RelativeSizeAxes = Axes.X,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Height = 512,
@@ -93,6 +95,12 @@ namespace osu.Game.Tournament.Screens.Gameplay
RelativeSizeAxes = Axes.X,
Text = "Toggle chat",
Action = () => { State.Value = State.Value == TourneyState.Idle ? TourneyState.Playing : TourneyState.Idle; }
+ },
+ new SettingsSlider
+ {
+ LabelText = "Chroma Width",
+ Bindable = LadderInfo.ChromaKeyWidth,
+ KeyboardStep = 1,
}
}
}
@@ -101,6 +109,8 @@ namespace osu.Game.Tournament.Screens.Gameplay
State.BindTo(ipc.State);
State.BindValueChanged(stateChanged, true);
+ ladder.ChromaKeyWidth.BindValueChanged(width => chroma.Width = width.NewValue, true);
+
currentMatch.BindValueChanged(m =>
{
warmup.Value = m.NewValue.Team1Score.Value + m.NewValue.Team2Score.Value == 0;
diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs
index 2b0bfe0b74..b4c6d589d7 100644
--- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs
+++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs
@@ -50,11 +50,11 @@ namespace osu.Game.Tournament.Screens.MapPool
new MatchHeader(),
mapFlows = new FillFlowContainer>
{
- Y = 140,
+ Y = 160,
Spacing = new Vector2(10, 10),
- Padding = new MarginPadding(25),
Direction = FillDirection.Vertical,
- RelativeSizeAxes = Axes.Both,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
},
new ControlPanel
{
@@ -95,6 +95,7 @@ namespace osu.Game.Tournament.Screens.MapPool
Text = "Reset",
Action = reset
},
+ new ControlPanel.Spacer(),
}
}
};
@@ -211,11 +212,15 @@ namespace osu.Game.Tournament.Screens.MapPool
{
mapFlows.Clear();
+ int totalRows = 0;
+
if (match.NewValue.Round.Value != null)
{
FillFlowContainer currentFlow = null;
string currentMod = null;
+ int flowCount = 0;
+
foreach (var b in match.NewValue.Round.Value.Beatmaps)
{
if (currentFlow == null || currentMod != b.Mods)
@@ -229,6 +234,15 @@ namespace osu.Game.Tournament.Screens.MapPool
});
currentMod = b.Mods;
+
+ totalRows++;
+ flowCount = 0;
+ }
+
+ if (++flowCount > 2)
+ {
+ totalRows++;
+ flowCount = 1;
}
currentFlow.Add(new TournamentBeatmapPanel(b.BeatmapInfo, b.Mods)
@@ -239,6 +253,12 @@ namespace osu.Game.Tournament.Screens.MapPool
});
}
}
+
+ mapFlows.Padding = new MarginPadding(5)
+ {
+ // remove horizontal padding to increase flow width to 3 panels
+ Horizontal = totalRows > 9 ? 0 : 100
+ };
}
}
}
diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs
index b7f8b2bfd6..c91379b2d6 100644
--- a/osu.Game.Tournament/Screens/SetupScreen.cs
+++ b/osu.Game.Tournament/Screens/SetupScreen.cs
@@ -116,7 +116,7 @@ namespace osu.Game.Tournament.Screens
{
windowSize.Value = new Size((int)(1920 / TournamentSceneManager.STREAM_AREA_WIDTH * TournamentSceneManager.REQUIRED_WIDTH), 1080);
}
- }
+ },
};
}
diff --git a/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs b/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs
index 6ad5ccaf0c..bd5aa2f5d9 100644
--- a/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs
+++ b/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Tournament.Screens.Showcase
Origin = Anchor.TopCentre,
FillMode = FillMode.Fit,
RelativeSizeAxes = Axes.Both,
- Texture = textures.Get("game-screen-logo"),
+ Texture = textures.Get("header-logo"),
};
}
}
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 31869f9310..abb3f8ac42 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -14,7 +14,6 @@ using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Graphics.Video;
using osu.Framework.Lists;
using osu.Framework.Logging;
using osu.Framework.Platform;
@@ -403,7 +402,6 @@ namespace osu.Game.Beatmaps
protected override IBeatmap GetBeatmap() => beatmap;
protected override Texture GetBackground() => null;
- protected override VideoSprite GetVideo() => null;
protected override Track GetTrack() => null;
}
diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
index 1991770518..e62a9bb39d 100644
--- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
@@ -6,7 +6,6 @@ using System.Linq;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Graphics.Video;
using osu.Framework.IO.Stores;
using osu.Framework.Logging;
using osu.Game.Beatmaps.Formats;
@@ -67,24 +66,6 @@ namespace osu.Game.Beatmaps
}
}
- protected override VideoSprite GetVideo()
- {
- if (Metadata?.VideoFile == null)
- return null;
-
- try
- {
- var stream = textureStore.GetStream(getPathForFile(Metadata.VideoFile));
-
- return stream == null ? null : new VideoSprite(stream);
- }
- catch (Exception e)
- {
- Logger.Error(e, "Video failed to load");
- return null;
- }
- }
-
protected override Track GetTrack()
{
try
diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs
index 9267527d79..001f319307 100644
--- a/osu.Game/Beatmaps/BeatmapMetadata.cs
+++ b/osu.Game/Beatmaps/BeatmapMetadata.cs
@@ -52,7 +52,6 @@ namespace osu.Game.Beatmaps
public int PreviewTime { get; set; }
public string AudioFile { get; set; }
public string BackgroundFile { get; set; }
- public string VideoFile { get; set; }
public override string ToString() => $"{Artist} - {Title} ({Author})";
@@ -82,8 +81,7 @@ namespace osu.Game.Beatmaps
&& Tags == other.Tags
&& PreviewTime == other.PreviewTime
&& AudioFile == other.AudioFile
- && BackgroundFile == other.BackgroundFile
- && VideoFile == other.VideoFile;
+ && BackgroundFile == other.BackgroundFile;
}
}
}
diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
index bfcc38e4a9..8080e94075 100644
--- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
@@ -7,7 +7,6 @@ using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Graphics.Video;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
@@ -45,8 +44,6 @@ namespace osu.Game.Beatmaps
protected override Texture GetBackground() => textures?.Get(@"Backgrounds/bg4");
- protected override VideoSprite GetVideo() => null;
-
protected override Track GetTrack() => GetVirtualTrack();
private class DummyRulesetInfo : RulesetInfo
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 4b01b2490e..f5b27eddd2 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -6,11 +6,11 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using osu.Framework.Extensions;
-using osu.Game.Beatmaps.Timing;
-using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Beatmaps.ControlPoints;
-using osu.Game.IO;
using osu.Game.Beatmaps.Legacy;
+using osu.Game.Beatmaps.Timing;
+using osu.Game.IO;
+using osu.Game.Rulesets.Objects.Legacy;
namespace osu.Game.Beatmaps.Formats
{
@@ -303,10 +303,6 @@ namespace osu.Game.Beatmaps.Formats
beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[2]);
break;
- case LegacyEventType.Video:
- beatmap.BeatmapInfo.Metadata.VideoFile = CleanFilename(split[2]);
- break;
-
case LegacyEventType.Break:
double start = getOffsetTime(Parsing.ParseDouble(split[1]));
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index 09f40ce7b6..ec2ca30535 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -133,9 +133,6 @@ namespace osu.Game.Beatmaps.Formats
if (!string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.BackgroundFile))
writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Background},0,\"{beatmap.BeatmapInfo.Metadata.BackgroundFile}\",0,0"));
- if (!string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.VideoFile))
- writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Video},0,\"{beatmap.BeatmapInfo.Metadata.VideoFile}\",0,0"));
-
foreach (var b in beatmap.Breaks)
writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Break},{b.StartTime},{b.EndTime}"));
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
index c81f933bca..269449ef80 100644
--- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
@@ -4,13 +4,13 @@
using System;
using System.Collections.Generic;
using System.IO;
-using osuTK;
-using osuTK.Graphics;
using osu.Framework.Graphics;
+using osu.Framework.Utils;
+using osu.Game.Beatmaps.Legacy;
using osu.Game.IO;
using osu.Game.Storyboards;
-using osu.Game.Beatmaps.Legacy;
-using osu.Framework.Utils;
+using osuTK;
+using osuTK.Graphics;
namespace osu.Game.Beatmaps.Formats
{
@@ -87,6 +87,15 @@ namespace osu.Game.Beatmaps.Formats
switch (type)
{
+ case LegacyEventType.Video:
+ {
+ var offset = Parsing.ParseInt(split[1]);
+ var path = CleanFilename(split[2]);
+
+ storyboard.GetLayer("Video").Add(new StoryboardVideo(path, offset));
+ break;
+ }
+
case LegacyEventType.Sprite:
{
var layer = parseLayer(split[1]);
diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs
index 5f1f0d1e40..31975157a0 100644
--- a/osu.Game/Beatmaps/IWorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs
@@ -1,10 +1,10 @@
// 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.Audio.Track;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Graphics.Video;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
@@ -26,11 +26,6 @@ namespace osu.Game.Beatmaps
///
Texture Background { get; }
- ///
- /// Retrieves the video background file for this .
- ///
- VideoSprite Video { get; }
-
///
/// Retrieves the audio track for this .
///
@@ -60,8 +55,9 @@ namespace osu.Game.Beatmaps
///
/// The to create a playable for.
/// The s to apply to the .
+ /// The maximum length in milliseconds to wait for load to complete. Defaults to 10,000ms.
/// The converted .
/// If could not be converted to .
- IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null);
+ IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null);
}
}
diff --git a/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs b/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs
index 5237445640..48e8bdbb76 100644
--- a/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs
+++ b/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs
@@ -8,6 +8,7 @@ namespace osu.Game.Beatmaps.Legacy
Background = 0,
Fail = 1,
Pass = 2,
- Foreground = 3
+ Foreground = 3,
+ Video = 4
}
}
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 1e1ffad81e..f30340956a 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -1,23 +1,22 @@
// 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.Audio.Track;
-using osu.Framework.Graphics.Textures;
-using osu.Game.Rulesets.Mods;
using System;
using System.Collections.Generic;
-using osu.Game.Storyboards;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Audio;
+using osu.Framework.Audio.Track;
+using osu.Framework.Graphics.Textures;
+using osu.Framework.Logging;
using osu.Framework.Statistics;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.UI;
using osu.Game.Skinning;
-using osu.Framework.Graphics.Video;
-using osu.Framework.Logging;
+using osu.Game.Storyboards;
namespace osu.Game.Beatmaps
{
@@ -83,55 +82,81 @@ namespace osu.Game.Beatmaps
/// The applicable .
protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap);
- public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null)
+ public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null)
{
- mods ??= Array.Empty();
-
- var rulesetInstance = ruleset.CreateInstance();
-
- IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance);
-
- // Check if the beatmap can be converted
- if (Beatmap.HitObjects.Count > 0 && !converter.CanConvert())
- throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter}).");
-
- // Apply conversion mods
- foreach (var mod in mods.OfType())
- mod.ApplyToBeatmapConverter(converter);
-
- // Convert
- IBeatmap converted = converter.Convert();
-
- // Apply difficulty mods
- if (mods.Any(m => m is IApplicableToDifficulty))
+ using (var cancellationSource = new CancellationTokenSource(timeout ?? TimeSpan.FromSeconds(10)))
{
- converted.BeatmapInfo = converted.BeatmapInfo.Clone();
- converted.BeatmapInfo.BaseDifficulty = converted.BeatmapInfo.BaseDifficulty.Clone();
+ mods ??= Array.Empty();
- foreach (var mod in mods.OfType())
- mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty);
- }
+ var rulesetInstance = ruleset.CreateInstance();
- IBeatmapProcessor processor = rulesetInstance.CreateBeatmapProcessor(converted);
+ IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance);
- processor?.PreProcess();
+ // Check if the beatmap can be converted
+ if (Beatmap.HitObjects.Count > 0 && !converter.CanConvert())
+ throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter}).");
- // Compute default values for hitobjects, including creating nested hitobjects in-case they're needed
- foreach (var obj in converted.HitObjects)
- obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty);
+ // Apply conversion mods
+ foreach (var mod in mods.OfType())
+ {
+ if (cancellationSource.IsCancellationRequested)
+ throw new BeatmapLoadTimeoutException(BeatmapInfo);
- foreach (var mod in mods.OfType())
- {
+ mod.ApplyToBeatmapConverter(converter);
+ }
+
+ // Convert
+ IBeatmap converted = converter.Convert();
+
+ // Apply difficulty mods
+ if (mods.Any(m => m is IApplicableToDifficulty))
+ {
+ converted.BeatmapInfo = converted.BeatmapInfo.Clone();
+ converted.BeatmapInfo.BaseDifficulty = converted.BeatmapInfo.BaseDifficulty.Clone();
+
+ foreach (var mod in mods.OfType())
+ {
+ if (cancellationSource.IsCancellationRequested)
+ throw new BeatmapLoadTimeoutException(BeatmapInfo);
+
+ mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty);
+ }
+ }
+
+ IBeatmapProcessor processor = rulesetInstance.CreateBeatmapProcessor(converted);
+
+ processor?.PreProcess();
+
+ // Compute default values for hitobjects, including creating nested hitobjects in-case they're needed
foreach (var obj in converted.HitObjects)
- mod.ApplyToHitObject(obj);
+ {
+ if (cancellationSource.IsCancellationRequested)
+ throw new BeatmapLoadTimeoutException(BeatmapInfo);
+
+ obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty);
+ }
+
+ foreach (var mod in mods.OfType())
+ {
+ foreach (var obj in converted.HitObjects)
+ {
+ if (cancellationSource.IsCancellationRequested)
+ throw new BeatmapLoadTimeoutException(BeatmapInfo);
+
+ mod.ApplyToHitObject(obj);
+ }
+ }
+
+ processor?.PostProcess();
+
+ foreach (var mod in mods.OfType())
+ {
+ cancellationSource.Token.ThrowIfCancellationRequested();
+ mod.ApplyToBeatmap(converted);
+ }
+
+ return converted;
}
-
- processor?.PostProcess();
-
- foreach (var mod in mods.OfType())
- mod.ApplyToBeatmap(converted);
-
- return converted;
}
private CancellationTokenSource loadCancellation = new CancellationTokenSource();
@@ -208,10 +233,6 @@ namespace osu.Game.Beatmaps
protected abstract Texture GetBackground();
private readonly RecyclableLazy background;
- public VideoSprite Video => GetVideo();
-
- protected abstract VideoSprite GetVideo();
-
public bool TrackLoaded => track.IsResultAvailable;
public Track Track => track.Value;
protected abstract Track GetTrack();
@@ -297,5 +318,13 @@ namespace osu.Game.Beatmaps
private void recreate() => lazy = new Lazy(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication);
}
+
+ private class BeatmapLoadTimeoutException : TimeoutException
+ {
+ public BeatmapLoadTimeoutException(BeatmapInfo beatmapInfo)
+ : base($"Timed out while loading beatmap ({beatmapInfo}).")
+ {
+ }
+ }
}
}
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index 21de654670..41f6747b74 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -70,7 +70,6 @@ namespace osu.Game.Configuration
Set(OsuSetting.ShowFpsDisplay, false);
Set(OsuSetting.ShowStoryboard, true);
- Set(OsuSetting.ShowVideoBackground, true);
Set(OsuSetting.BeatmapSkins, true);
Set(OsuSetting.BeatmapHitsounds, true);
@@ -176,7 +175,6 @@ namespace osu.Game.Configuration
BlurLevel,
LightenDuringBreaks,
ShowStoryboard,
- ShowVideoBackground,
KeyOverlay,
ScoreMeter,
FloatingComments,
diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs
index 65c104b92f..4485ce3447 100644
--- a/osu.Game/Graphics/Containers/UserDimContainer.cs
+++ b/osu.Game/Graphics/Containers/UserDimContainer.cs
@@ -55,8 +55,6 @@ namespace osu.Game.Graphics.Containers
protected Bindable ShowStoryboard { get; private set; }
- protected Bindable ShowVideo { get; private set; }
-
private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? BREAK_LIGHTEN_AMOUNT : 0;
protected float DimLevel => Math.Max(EnableUserDim.Value && !IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0);
@@ -79,14 +77,12 @@ namespace osu.Game.Graphics.Containers
UserDimLevel = config.GetBindable(OsuSetting.DimLevel);
LightenDuringBreaks = config.GetBindable(OsuSetting.LightenDuringBreaks);
ShowStoryboard = config.GetBindable(OsuSetting.ShowStoryboard);
- ShowVideo = config.GetBindable(OsuSetting.ShowVideoBackground);
EnableUserDim.ValueChanged += _ => UpdateVisuals();
UserDimLevel.ValueChanged += _ => UpdateVisuals();
LightenDuringBreaks.ValueChanged += _ => UpdateVisuals();
IsBreakTime.ValueChanged += _ => UpdateVisuals();
ShowStoryboard.ValueChanged += _ => UpdateVisuals();
- ShowVideo.ValueChanged += _ => UpdateVisuals();
StoryboardReplacesBackground.ValueChanged += _ => UpdateVisuals();
IgnoreUserSettings.ValueChanged += _ => UpdateVisuals();
}
diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs
index 984f5e52d1..466d59b08b 100644
--- a/osu.Game/Graphics/OsuColour.cs
+++ b/osu.Game/Graphics/OsuColour.cs
@@ -3,6 +3,8 @@
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Scoring;
using osuTK.Graphics;
namespace osu.Game.Graphics
@@ -37,6 +39,61 @@ namespace osu.Game.Graphics
}
}
+ ///
+ /// Retrieves the colour for a .
+ ///
+ public static Color4 ForRank(ScoreRank rank)
+ {
+ switch (rank)
+ {
+ case ScoreRank.XH:
+ case ScoreRank.X:
+ return Color4Extensions.FromHex(@"de31ae");
+
+ case ScoreRank.SH:
+ case ScoreRank.S:
+ return Color4Extensions.FromHex(@"02b5c3");
+
+ case ScoreRank.A:
+ return Color4Extensions.FromHex(@"88da20");
+
+ case ScoreRank.B:
+ return Color4Extensions.FromHex(@"e3b130");
+
+ case ScoreRank.C:
+ return Color4Extensions.FromHex(@"ff8e5d");
+
+ default:
+ return Color4Extensions.FromHex(@"ff5a5a");
+ }
+ }
+
+ ///
+ /// Retrieves the colour for a .
+ ///
+ public Color4 ForHitResult(HitResult judgement)
+ {
+ switch (judgement)
+ {
+ case HitResult.Perfect:
+ case HitResult.Great:
+ return Blue;
+
+ case HitResult.Ok:
+ case HitResult.Good:
+ return Green;
+
+ case HitResult.Meh:
+ return Yellow;
+
+ case HitResult.Miss:
+ return Red;
+
+ default:
+ return Color4.White;
+ }
+ }
+
// See https://github.com/ppy/osu-web/blob/master/resources/assets/less/colors.less
public readonly Color4 PurpleLighter = Color4Extensions.FromHex(@"eeeeff");
public readonly Color4 PurpleLight = Color4Extensions.FromHex(@"aa88ff");
diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs
index 12688da9df..4aea5aa518 100644
--- a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs
+++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs
@@ -43,6 +43,18 @@ namespace osu.Game.Graphics.Sprites
set => blurredText.Colour = value;
}
+ public Vector2 Spacing
+ {
+ get => spriteText.Spacing;
+ set => spriteText.Spacing = blurredText.Spacing = value;
+ }
+
+ public bool UseFullGlyphHeight
+ {
+ get => spriteText.UseFullGlyphHeight;
+ set => spriteText.UseFullGlyphHeight = blurredText.UseFullGlyphHeight = value;
+ }
+
public GlowingSpriteText()
{
AutoSizeAxes = Axes.Both;
diff --git a/osu.Game/Graphics/Sprites/OsuSpriteText.cs b/osu.Game/Graphics/Sprites/OsuSpriteText.cs
index cd988c347b..76e46513ba 100644
--- a/osu.Game/Graphics/Sprites/OsuSpriteText.cs
+++ b/osu.Game/Graphics/Sprites/OsuSpriteText.cs
@@ -1,9 +1,7 @@
// 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.Graphics;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Transforms;
namespace osu.Game.Graphics.Sprites
{
@@ -15,23 +13,4 @@ namespace osu.Game.Graphics.Sprites
Font = OsuFont.Default;
}
}
-
- public static class OsuSpriteTextTransformExtensions
- {
- ///
- /// Sets Text to a new value after a duration.
- ///
- /// A to which further transforms can be added.
- public static TransformSequence TransformTextTo(this T spriteText, string newText, double duration = 0, Easing easing = Easing.None)
- where T : OsuSpriteText
- => spriteText.TransformTo(nameof(OsuSpriteText.Text), newText, duration, easing);
-
- ///
- /// Sets Text to a new value after a duration.
- ///
- /// A to which further transforms can be added.
- public static TransformSequence TransformTextTo(this TransformSequence t, string newText, double duration = 0, Easing easing = Easing.None)
- where T : OsuSpriteText
- => t.Append(o => o.TransformTextTo(newText, duration, easing));
- }
}
diff --git a/osu.Game/Graphics/UserInterface/LoadingSpinner.cs b/osu.Game/Graphics/UserInterface/LoadingSpinner.cs
index 36d429b8c1..4f4607c114 100644
--- a/osu.Game/Graphics/UserInterface/LoadingSpinner.cs
+++ b/osu.Game/Graphics/UserInterface/LoadingSpinner.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Graphics.UserInterface
protected Container MainContents;
- protected const float TRANSITION_DURATION = 500;
+ public const float TRANSITION_DURATION = 500;
private const float spin_duration = 900;
@@ -27,7 +27,8 @@ namespace osu.Game.Graphics.UserInterface
/// Constuct a new loading spinner.
///
/// Whether the spinner should have a surrounding black box for visibility.
- public LoadingSpinner(bool withBox = false)
+ /// Whether colours should be inverted (black spinner instead of white).
+ public LoadingSpinner(bool withBox = false, bool inverted = false)
{
Size = new Vector2(60);
@@ -45,7 +46,7 @@ namespace osu.Game.Graphics.UserInterface
{
new Box
{
- Colour = Color4.Black,
+ Colour = inverted ? Color4.White : Color4.Black,
RelativeSizeAxes = Axes.Both,
Alpha = withBox ? 0.7f : 0
},
@@ -53,6 +54,7 @@ namespace osu.Game.Graphics.UserInterface
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
+ Colour = inverted ? Color4.Black : Color4.White,
Scale = new Vector2(withBox ? 0.6f : 1),
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.Solid.CircleNotch
diff --git a/osu.Game/IO/Archives/ArchiveReader.cs b/osu.Game/IO/Archives/ArchiveReader.cs
index 4ee7a19ebc..a30f961daf 100644
--- a/osu.Game/IO/Archives/ArchiveReader.cs
+++ b/osu.Game/IO/Archives/ArchiveReader.cs
@@ -45,7 +45,5 @@ namespace osu.Game.IO.Archives
return buffer;
}
}
-
- public abstract Stream GetUnderlyingStream();
}
}
diff --git a/osu.Game/IO/Archives/LegacyByteArrayReader.cs b/osu.Game/IO/Archives/LegacyByteArrayReader.cs
new file mode 100644
index 0000000000..0c3620403f
--- /dev/null
+++ b/osu.Game/IO/Archives/LegacyByteArrayReader.cs
@@ -0,0 +1,30 @@
+// 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.IO;
+
+namespace osu.Game.IO.Archives
+{
+ ///
+ /// Allows reading a single file from the provided stream.
+ ///
+ public class LegacyByteArrayReader : ArchiveReader
+ {
+ private readonly byte[] content;
+
+ public LegacyByteArrayReader(byte[] content, string filename)
+ : base(filename)
+ {
+ this.content = content;
+ }
+
+ public override Stream GetStream(string name) => new MemoryStream(content);
+
+ public override void Dispose()
+ {
+ }
+
+ public override IEnumerable Filenames => new[] { Name };
+ }
+}
diff --git a/osu.Game/IO/Archives/LegacyDirectoryArchiveReader.cs b/osu.Game/IO/Archives/LegacyDirectoryArchiveReader.cs
index eff02ae7a5..dfae58aed7 100644
--- a/osu.Game/IO/Archives/LegacyDirectoryArchiveReader.cs
+++ b/osu.Game/IO/Archives/LegacyDirectoryArchiveReader.cs
@@ -28,7 +28,5 @@ namespace osu.Game.IO.Archives
}
public override IEnumerable Filenames => Directory.GetFiles(path, "*", SearchOption.AllDirectories).Select(f => f.Replace(path, string.Empty).Trim(Path.DirectorySeparatorChar)).ToArray();
-
- public override Stream GetUnderlyingStream() => null;
}
}
diff --git a/osu.Game/IO/Archives/LegacyFileArchiveReader.cs b/osu.Game/IO/Archives/LegacyFileArchiveReader.cs
index bd5f9cbd07..72e5a21079 100644
--- a/osu.Game/IO/Archives/LegacyFileArchiveReader.cs
+++ b/osu.Game/IO/Archives/LegacyFileArchiveReader.cs
@@ -28,7 +28,5 @@ namespace osu.Game.IO.Archives
}
public override IEnumerable Filenames => new[] { Name };
-
- public override Stream GetUnderlyingStream() => null;
}
}
diff --git a/osu.Game/IO/Archives/ZipArchiveReader.cs b/osu.Game/IO/Archives/ZipArchiveReader.cs
index 35f38ea7e8..80dfa104f3 100644
--- a/osu.Game/IO/Archives/ZipArchiveReader.cs
+++ b/osu.Game/IO/Archives/ZipArchiveReader.cs
@@ -45,7 +45,5 @@ namespace osu.Game.IO.Archives
}
public override IEnumerable Filenames => archive.Entries.Select(e => e.Key).ExcludeSystemFileNames();
-
- public override Stream GetUnderlyingStream() => archiveStream;
}
}
diff --git a/osu.Game/Online/Chat/ChannelType.cs b/osu.Game/Online/Chat/ChannelType.cs
index 7d2b661164..151efc4645 100644
--- a/osu.Game/Online/Chat/ChannelType.cs
+++ b/osu.Game/Online/Chat/ChannelType.cs
@@ -12,5 +12,6 @@ namespace osu.Game.Online.Chat
Temporary,
PM,
Group,
+ System,
}
}
diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs
index 0914f688e9..4fbeac1db9 100644
--- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs
+++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs
@@ -26,7 +26,7 @@ namespace osu.Game.Online.Chat
protected ChannelManager ChannelManager;
- private DrawableChannel drawableChannel;
+ private StandAloneDrawableChannel drawableChannel;
private readonly bool postingTextbox;
@@ -77,6 +77,9 @@ namespace osu.Game.Online.Chat
ChannelManager = manager;
}
+ protected virtual StandAloneDrawableChannel CreateDrawableChannel(Channel channel) =>
+ new StandAloneDrawableChannel(channel);
+
private void postMessage(TextBox sender, bool newtext)
{
var text = textbox.Text.Trim();
@@ -100,14 +103,14 @@ namespace osu.Game.Online.Chat
if (e.NewValue == null) return;
- AddInternal(drawableChannel = new StandAloneDrawableChannel(e.NewValue)
- {
- CreateChatLineAction = CreateMessage,
- Padding = new MarginPadding { Bottom = postingTextbox ? textbox_height : 0 }
- });
+ drawableChannel = CreateDrawableChannel(e.NewValue);
+ drawableChannel.CreateChatLineAction = CreateMessage;
+ drawableChannel.Padding = new MarginPadding { Bottom = postingTextbox ? textbox_height : 0 };
+
+ AddInternal(drawableChannel);
}
- protected class StandAloneDrawableChannel : DrawableChannel
+ public class StandAloneDrawableChannel : DrawableChannel
{
public Func CreateChatLineAction;
diff --git a/osu.Game/Online/Leaderboards/DrawableRank.cs b/osu.Game/Online/Leaderboards/DrawableRank.cs
index 45b91bbf81..4d41230799 100644
--- a/osu.Game/Online/Leaderboards/DrawableRank.cs
+++ b/osu.Game/Online/Leaderboards/DrawableRank.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Online.Leaderboards
FillMode = FillMode.Fit;
FillAspectRatio = 2;
- var rankColour = getRankColour();
+ var rankColour = OsuColour.ForRank(rank);
InternalChild = new DrawSizePreservingFillContainer
{
TargetDrawSize = new Vector2(64, 32),
@@ -59,7 +59,7 @@ namespace osu.Game.Online.Leaderboards
Padding = new MarginPadding { Top = 5 },
Colour = getRankNameColour(),
Font = OsuFont.Numeric.With(size: 25),
- Text = getRankName(),
+ Text = GetRankName(rank),
ShadowColour = Color4.Black.Opacity(0.3f),
ShadowOffset = new Vector2(0, 0.08f),
Shadow = true,
@@ -69,36 +69,7 @@ namespace osu.Game.Online.Leaderboards
};
}
- private string getRankName() => rank.GetDescription().TrimEnd('+');
-
- ///
- /// Retrieves the grade background colour.
- ///
- private Color4 getRankColour()
- {
- switch (rank)
- {
- case ScoreRank.XH:
- case ScoreRank.X:
- return Color4Extensions.FromHex(@"ce1c9d");
-
- case ScoreRank.SH:
- case ScoreRank.S:
- return Color4Extensions.FromHex(@"00a8b5");
-
- case ScoreRank.A:
- return Color4Extensions.FromHex(@"7cce14");
-
- case ScoreRank.B:
- return Color4Extensions.FromHex(@"e3b130");
-
- case ScoreRank.C:
- return Color4Extensions.FromHex(@"f18252");
-
- default:
- return Color4Extensions.FromHex(@"e95353");
- }
- }
+ public static string GetRankName(ScoreRank rank) => rank.GetDescription().TrimEnd('+');
///
/// Retrieves the grade text colour.
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 3c7ab27651..5487bd9320 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -47,6 +47,8 @@ namespace osu.Game
{
public const string CLIENT_STREAM_NAME = "lazer";
+ public const int SAMPLE_CONCURRENCY = 6;
+
protected OsuConfigManager LocalConfig;
protected BeatmapManager BeatmapManager;
@@ -153,6 +155,8 @@ namespace osu.Game
AddFont(Resources, @"Fonts/Venera-Bold");
AddFont(Resources, @"Fonts/Venera-Black");
+ Audio.Samples.PlaybackConcurrency = SAMPLE_CONCURRENCY;
+
runMigrations();
dependencies.Cache(SkinManager = new SkinManager(Storage, contextFactory, Host, Audio, new NamespacedResourceStore(Resources, "Skins/Legacy")));
diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs
index 443f2b7bf7..6019657cf0 100644
--- a/osu.Game/Overlays/Chat/DrawableChannel.cs
+++ b/osu.Game/Overlays/Chat/DrawableChannel.cs
@@ -26,6 +26,20 @@ namespace osu.Game.Overlays.Chat
protected FillFlowContainer ChatLineFlow;
private OsuScrollContainer scroll;
+ private bool scrollbarVisible = true;
+
+ public bool ScrollbarVisible
+ {
+ set
+ {
+ if (scrollbarVisible == value) return;
+
+ scrollbarVisible = value;
+ if (scroll != null)
+ scroll.ScrollbarVisible = value;
+ }
+ }
+
[Resolved]
private OsuColour colours { get; set; }
@@ -44,6 +58,7 @@ namespace osu.Game.Overlays.Chat
Masking = true,
Child = scroll = new OsuScrollContainer
{
+ ScrollbarVisible = scrollbarVisible,
RelativeSizeAxes = Axes.Both,
// Some chat lines have effects that slightly protrude to the bottom,
// which we do not want to mask away, hence the padding.
diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs
index d5d9a6c2ce..e3ede04edd 100644
--- a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs
+++ b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs
@@ -39,6 +39,7 @@ namespace osu.Game.Overlays.Chat.Tabs
public ChannelSelectorTabChannel()
{
Name = "+";
+ Type = ChannelType.System;
}
}
}
diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs
index 104495ae01..a72f182450 100644
--- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs
+++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs
@@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Chat.Tabs
// performTabSort might've made selectorTab's position wonky, fix it
TabContainer.SetLayoutPosition(selectorTab, float.MaxValue);
- ((ChannelTabItem)item).OnRequestClose += tabCloseRequested;
+ ((ChannelTabItem)item).OnRequestClose += channelItem => OnRequestLeave?.Invoke(channelItem.Value);
base.AddTabItem(item, addToDropdown);
}
@@ -74,18 +74,24 @@ namespace osu.Game.Overlays.Chat.Tabs
///
/// Removes a channel from the ChannelTabControl.
- /// If the selected channel is the one that is beeing removed, the next available channel will be selected.
+ /// If the selected channel is the one that is being removed, the next available channel will be selected.
///
/// The channel that is going to be removed.
public void RemoveChannel(Channel channel)
{
- RemoveItem(channel);
-
if (Current.Value == channel)
{
- // Prefer non-selector channels first
- Current.Value = Items.FirstOrDefault(c => !(c is ChannelSelectorTabItem.ChannelSelectorTabChannel)) ?? Items.FirstOrDefault();
+ var allChannels = TabContainer.AllTabItems.Select(tab => tab.Value).ToList();
+ var isNextTabSelector = allChannels[allChannels.IndexOf(channel) + 1] == selectorTab.Value;
+
+ // selectorTab is not switchable, so we have to explicitly select it if it's the only tab left
+ if (isNextTabSelector && allChannels.Count == 2)
+ SelectTab(selectorTab);
+ else
+ SwitchTab(isNextTabSelector ? -1 : 1);
}
+
+ RemoveItem(channel);
}
protected override void SelectTab(TabItem tab)
@@ -100,21 +106,6 @@ namespace osu.Game.Overlays.Chat.Tabs
selectorTab.Active.Value = false;
}
- private void tabCloseRequested(TabItem tab)
- {
- int totalTabs = TabContainer.Count - 1; // account for selectorTab
- int currentIndex = Math.Clamp(TabContainer.IndexOf(tab), 1, totalTabs);
-
- if (tab == SelectedTab && totalTabs > 1)
- // Select the tab after tab-to-be-removed's index, or the tab before if current == last
- SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]);
- else if (totalTabs == 1 && !selectorTab.Active.Value)
- // Open channel selection overlay if all channel tabs will be closed after removing this tab
- SelectTab(selectorTab);
-
- OnRequestLeave?.Invoke(tab.Value);
- }
-
protected override TabFillFlowContainer CreateTabFlow() => new ChannelTabFillFlowContainer
{
Direction = FillDirection.Full,
diff --git a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs
new file mode 100644
index 0000000000..3c9b31daae
--- /dev/null
+++ b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs
@@ -0,0 +1,267 @@
+// 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 System.Threading;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
+using osu.Game.Users;
+using osuTK;
+
+namespace osu.Game.Overlays.Dashboard.Friends
+{
+ public class FriendDisplay : CompositeDrawable
+ {
+ private List users = new List();
+
+ public List Users
+ {
+ get => users;
+ set
+ {
+ users = value;
+
+ onlineStreamControl.Populate(value);
+ }
+ }
+
+ [Resolved]
+ private IAPIProvider api { get; set; }
+
+ private GetFriendsRequest request;
+ private CancellationTokenSource cancellationToken;
+
+ private Drawable currentContent;
+
+ private readonly FriendOnlineStreamControl onlineStreamControl;
+ private readonly Box background;
+ private readonly Box controlBackground;
+ private readonly UserListToolbar userListToolbar;
+ private readonly Container itemsPlaceholder;
+ private readonly LoadingLayer loading;
+
+ public FriendDisplay()
+ {
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+ InternalChild = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ controlBackground = new Box
+ {
+ RelativeSizeAxes = Axes.Both
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Padding = new MarginPadding
+ {
+ Top = 20,
+ Horizontal = 45
+ },
+ Child = onlineStreamControl = new FriendOnlineStreamControl(),
+ }
+ }
+ },
+ new Container
+ {
+ Name = "User List",
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ background = new Box
+ {
+ RelativeSizeAxes = Axes.Both
+ },
+ new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Margin = new MarginPadding { Bottom = 20 },
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Padding = new MarginPadding
+ {
+ Horizontal = 40,
+ Vertical = 20
+ },
+ Child = userListToolbar = new UserListToolbar
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ }
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ itemsPlaceholder = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Padding = new MarginPadding { Horizontal = 50 }
+ },
+ loading = new LoadingLayer(itemsPlaceholder)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OverlayColourProvider colourProvider)
+ {
+ background.Colour = colourProvider.Background4;
+ controlBackground.Colour = colourProvider.Background5;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ onlineStreamControl.Current.BindValueChanged(_ => recreatePanels());
+ userListToolbar.DisplayStyle.BindValueChanged(_ => recreatePanels());
+ userListToolbar.SortCriteria.BindValueChanged(_ => recreatePanels());
+ }
+
+ public void Fetch()
+ {
+ if (!api.IsLoggedIn)
+ return;
+
+ request = new GetFriendsRequest();
+ request.Success += response => Schedule(() => Users = response);
+ api.Queue(request);
+ }
+
+ private void recreatePanels()
+ {
+ if (!users.Any())
+ return;
+
+ cancellationToken?.Cancel();
+
+ if (itemsPlaceholder.Any())
+ loading.Show();
+
+ var sortedUsers = sortUsers(getUsersInCurrentGroup());
+
+ LoadComponentAsync(createTable(sortedUsers), addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token);
+ }
+
+ private List getUsersInCurrentGroup()
+ {
+ switch (onlineStreamControl.Current.Value?.Status)
+ {
+ default:
+ case OnlineStatus.All:
+ return users;
+
+ case OnlineStatus.Offline:
+ return users.Where(u => !u.IsOnline).ToList();
+
+ case OnlineStatus.Online:
+ return users.Where(u => u.IsOnline).ToList();
+ }
+ }
+
+ private void addContentToPlaceholder(Drawable content)
+ {
+ loading.Hide();
+
+ var lastContent = currentContent;
+
+ if (lastContent != null)
+ {
+ lastContent.FadeOut(100, Easing.OutQuint).Expire();
+ lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y);
+ }
+
+ itemsPlaceholder.Add(currentContent = content);
+ currentContent.FadeIn(200, Easing.OutQuint);
+ }
+
+ private FillFlowContainer createTable(List users)
+ {
+ var style = userListToolbar.DisplayStyle.Value;
+
+ return new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Spacing = new Vector2(style == OverlayPanelDisplayStyle.Card ? 10 : 2),
+ Children = users.Select(u => createUserPanel(u, style)).ToList()
+ };
+ }
+
+ private UserPanel createUserPanel(User user, OverlayPanelDisplayStyle style)
+ {
+ switch (style)
+ {
+ default:
+ case OverlayPanelDisplayStyle.Card:
+ return new UserGridPanel(user).With(panel =>
+ {
+ panel.Anchor = Anchor.TopCentre;
+ panel.Origin = Anchor.TopCentre;
+ panel.Width = 290;
+ });
+
+ case OverlayPanelDisplayStyle.List:
+ return new UserListPanel(user);
+ }
+ }
+
+ private List sortUsers(List unsorted)
+ {
+ switch (userListToolbar.SortCriteria.Value)
+ {
+ default:
+ case UserSortCriteria.LastVisit:
+ return unsorted.OrderByDescending(u => u.LastVisit).ToList();
+
+ case UserSortCriteria.Rank:
+ return unsorted.OrderByDescending(u => u.CurrentModeRank.HasValue).ThenBy(u => u.CurrentModeRank ?? 0).ToList();
+
+ case UserSortCriteria.Username:
+ return unsorted.OrderBy(u => u.Username).ToList();
+ }
+ }
+
+ protected override void Dispose(bool isDisposing)
+ {
+ request?.Cancel();
+ cancellationToken?.Cancel();
+
+ base.Dispose(isDisposing);
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Dashboard/Friends/FriendOnlineStreamControl.cs b/osu.Game/Overlays/Dashboard/Friends/FriendOnlineStreamControl.cs
new file mode 100644
index 0000000000..28546ceab8
--- /dev/null
+++ b/osu.Game/Overlays/Dashboard/Friends/FriendOnlineStreamControl.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 System.Collections.Generic;
+using System.Linq;
+using osu.Game.Users;
+
+namespace osu.Game.Overlays.Dashboard.Friends
+{
+ public class FriendOnlineStreamControl : OverlayStreamControl
+ {
+ protected override OverlayStreamItem CreateStreamItem(FriendStream value) => new FriendsOnlineStatusItem(value);
+
+ public void Populate(List users)
+ {
+ Clear();
+
+ var userCount = users.Count;
+ var onlineUsersCount = users.Count(user => user.IsOnline);
+
+ AddItem(new FriendStream(OnlineStatus.All, userCount));
+ AddItem(new FriendStream(OnlineStatus.Online, onlineUsersCount));
+ AddItem(new FriendStream(OnlineStatus.Offline, userCount - onlineUsersCount));
+
+ Current.Value = Items.FirstOrDefault();
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Dashboard/Friends/FriendStream.cs b/osu.Game/Overlays/Dashboard/Friends/FriendStream.cs
new file mode 100644
index 0000000000..4abece9a8d
--- /dev/null
+++ b/osu.Game/Overlays/Dashboard/Friends/FriendStream.cs
@@ -0,0 +1,18 @@
+// 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.Dashboard.Friends
+{
+ public class FriendStream
+ {
+ public OnlineStatus Status { get; }
+
+ public int Count { get; }
+
+ public FriendStream(OnlineStatus status, int count)
+ {
+ Status = status;
+ Count = count;
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Home/Friends/FriendsOnlineStatusItem.cs b/osu.Game/Overlays/Dashboard/Friends/FriendsOnlineStatusItem.cs
similarity index 78%
rename from osu.Game/Overlays/Home/Friends/FriendsOnlineStatusItem.cs
rename to osu.Game/Overlays/Dashboard/Friends/FriendsOnlineStatusItem.cs
index d9b780ce46..7e902203f8 100644
--- a/osu.Game/Overlays/Home/Friends/FriendsOnlineStatusItem.cs
+++ b/osu.Game/Overlays/Dashboard/Friends/FriendsOnlineStatusItem.cs
@@ -5,11 +5,11 @@ using System;
using osu.Game.Graphics;
using osuTK.Graphics;
-namespace osu.Game.Overlays.Home.Friends
+namespace osu.Game.Overlays.Dashboard.Friends
{
- public class FriendsOnlineStatusItem : OverlayStreamItem
+ public class FriendsOnlineStatusItem : OverlayStreamItem
{
- public FriendsOnlineStatusItem(FriendsBundle value)
+ public FriendsOnlineStatusItem(FriendStream value)
: base(value)
{
}
@@ -22,13 +22,13 @@ namespace osu.Game.Overlays.Home.Friends
{
switch (Value.Status)
{
- case FriendsOnlineStatus.All:
+ case OnlineStatus.All:
return Color4.White;
- case FriendsOnlineStatus.Online:
+ case OnlineStatus.Online:
return colours.GreenLight;
- case FriendsOnlineStatus.Offline:
+ case OnlineStatus.Offline:
return Color4.Black;
default:
diff --git a/osu.Game/Overlays/Dashboard/Friends/OnlineStatus.cs b/osu.Game/Overlays/Dashboard/Friends/OnlineStatus.cs
new file mode 100644
index 0000000000..6f2f55a6ed
--- /dev/null
+++ b/osu.Game/Overlays/Dashboard/Friends/OnlineStatus.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.Overlays.Dashboard.Friends
+{
+ public enum OnlineStatus
+ {
+ All,
+ Online,
+ Offline
+ }
+}
diff --git a/osu.Game/Overlays/Home/Friends/UserListToolbar.cs b/osu.Game/Overlays/Dashboard/Friends/UserListToolbar.cs
similarity index 96%
rename from osu.Game/Overlays/Home/Friends/UserListToolbar.cs
rename to osu.Game/Overlays/Dashboard/Friends/UserListToolbar.cs
index f7c5e9f4fd..fb4b938183 100644
--- a/osu.Game/Overlays/Home/Friends/UserListToolbar.cs
+++ b/osu.Game/Overlays/Dashboard/Friends/UserListToolbar.cs
@@ -6,7 +6,7 @@ using osu.Framework.Graphics.Containers;
using osuTK;
using osu.Framework.Bindables;
-namespace osu.Game.Overlays.Home.Friends
+namespace osu.Game.Overlays.Dashboard.Friends
{
public class UserListToolbar : CompositeDrawable
{
diff --git a/osu.Game/Overlays/Home/Friends/UserSortTabControl.cs b/osu.Game/Overlays/Dashboard/Friends/UserSortTabControl.cs
similarity index 90%
rename from osu.Game/Overlays/Home/Friends/UserSortTabControl.cs
rename to osu.Game/Overlays/Dashboard/Friends/UserSortTabControl.cs
index 2479fa4638..3a5f65212d 100644
--- a/osu.Game/Overlays/Home/Friends/UserSortTabControl.cs
+++ b/osu.Game/Overlays/Dashboard/Friends/UserSortTabControl.cs
@@ -3,7 +3,7 @@
using System.ComponentModel;
-namespace osu.Game.Overlays.Home.Friends
+namespace osu.Game.Overlays.Dashboard.Friends
{
public class UserSortTabControl : OverlaySortTabControl
{
diff --git a/osu.Game/Overlays/Home/Friends/FriendsBundle.cs b/osu.Game/Overlays/Home/Friends/FriendsBundle.cs
deleted file mode 100644
index 75d00dfef8..0000000000
--- a/osu.Game/Overlays/Home/Friends/FriendsBundle.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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
deleted file mode 100644
index 196f01ab4a..0000000000
--- a/osu.Game/Overlays/Home/Friends/FriendsOnlineStatusControl.cs
+++ /dev/null
@@ -1,26 +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 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/News/NewsArticleCover.cs b/osu.Game/Overlays/News/NewsArticleCover.cs
index e381b629e4..cca0cfb4a0 100644
--- a/osu.Game/Overlays/News/NewsArticleCover.cs
+++ b/osu.Game/Overlays/News/NewsArticleCover.cs
@@ -148,7 +148,7 @@ namespace osu.Game.Overlays.News
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Font = OsuFont.GetFont(Typeface.Torus, 12, FontWeight.Black, false, false),
+ Font = OsuFont.GetFont(Typeface.Torus, 12, FontWeight.Bold, false, false),
Text = date.ToString("d MMM yyy").ToUpper(),
Margin = new MarginPadding
{
diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs
index 17a2d4cf9f..c2a958b65e 100644
--- a/osu.Game/Overlays/Notifications/NotificationSection.cs
+++ b/osu.Game/Overlays/Notifications/NotificationSection.cs
@@ -84,13 +84,13 @@ namespace osu.Game.Overlays.Notifications
new OsuSpriteText
{
Text = titleText.ToUpperInvariant(),
- Font = OsuFont.GetFont(weight: FontWeight.Black)
+ Font = OsuFont.GetFont(weight: FontWeight.Bold)
},
countDrawable = new OsuSpriteText
{
Text = "3",
Colour = colours.Yellow,
- Font = OsuFont.GetFont(weight: FontWeight.Black)
+ Font = OsuFont.GetFont(weight: FontWeight.Bold)
},
}
},
diff --git a/osu.Game/Overlays/OSD/Toast.cs b/osu.Game/Overlays/OSD/Toast.cs
index 46c53ec409..5d36cac20e 100644
--- a/osu.Game/Overlays/OSD/Toast.cs
+++ b/osu.Game/Overlays/OSD/Toast.cs
@@ -53,7 +53,7 @@ namespace osu.Game.Overlays.OSD
{
Padding = new MarginPadding(10),
Name = "Description",
- Font = OsuFont.GetFont(size: 14, weight: FontWeight.Black),
+ Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold),
Spacing = new Vector2(1, 0),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
diff --git a/osu.Game/Overlays/OverlayStreamItem.cs b/osu.Game/Overlays/OverlayStreamItem.cs
index 630d3a0a22..7f8559e7de 100644
--- a/osu.Game/Overlays/OverlayStreamItem.cs
+++ b/osu.Game/Overlays/OverlayStreamItem.cs
@@ -59,7 +59,7 @@ namespace osu.Game.Overlays
new OsuSpriteText
{
Text = MainText,
- Font = OsuFont.GetFont(size: 12, weight: FontWeight.Black),
+ Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
},
new OsuSpriteText
{
diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs
index bf0e073350..52b712a40e 100644
--- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs
@@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
{
Text = "ACCOUNT",
Margin = new MarginPadding { Bottom = 5 },
- Font = OsuFont.GetFont(weight: FontWeight.Black),
+ Font = OsuFont.GetFont(weight: FontWeight.Bold),
},
form = new LoginForm
{
@@ -143,7 +143,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
},
},
},
- panel = new UserPanel(api.LocalUser.Value)
+ panel = new UserGridPanel(api.LocalUser.Value)
{
RelativeSizeAxes = Axes.X,
Action = RequestHide
diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs
index ea2811e5cd..3089040f96 100644
--- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs
@@ -18,15 +18,10 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
{
new SettingsCheckbox
{
- LabelText = "Storyboards",
+ LabelText = "Storyboard / Video",
Bindable = config.GetBindable(OsuSetting.ShowStoryboard)
},
new SettingsCheckbox
- {
- LabelText = "Video",
- Bindable = config.GetBindable(OsuSetting.ShowVideoBackground)
- },
- new SettingsCheckbox
{
LabelText = "Hit Lighting",
Bindable = config.GetBindable(OsuSetting.HitLighting)
diff --git a/osu.Game/Overlays/Settings/SettingsCheckbox.cs b/osu.Game/Overlays/Settings/SettingsCheckbox.cs
index a554159fd7..437b2e45b3 100644
--- a/osu.Game/Overlays/Settings/SettingsCheckbox.cs
+++ b/osu.Game/Overlays/Settings/SettingsCheckbox.cs
@@ -8,16 +8,14 @@ namespace osu.Game.Overlays.Settings
{
public class SettingsCheckbox : SettingsItem
{
- private OsuCheckbox checkbox;
-
private string labelText;
- protected override Drawable CreateControl() => checkbox = new OsuCheckbox();
+ protected override Drawable CreateControl() => new OsuCheckbox();
public override string LabelText
{
get => labelText;
- set => checkbox.LabelText = labelText = value;
+ set => ((OsuCheckbox)Control).LabelText = labelText = value;
}
}
}
diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs
index e89f2adf0b..c2dd40d2a6 100644
--- a/osu.Game/Overlays/Settings/SettingsItem.cs
+++ b/osu.Game/Overlays/Settings/SettingsItem.cs
@@ -33,22 +33,24 @@ namespace osu.Game.Overlays.Settings
protected readonly FillFlowContainer FlowContent;
- private SpriteText text;
+ private SpriteText labelText;
public bool ShowsDefaultIndicator = true;
public virtual string LabelText
{
- get => text?.Text ?? string.Empty;
+ get => labelText?.Text ?? string.Empty;
set
{
- if (text == null)
+ if (labelText == null)
{
// construct lazily for cases where the label is not needed (may be provided by the Control).
- FlowContent.Insert(-1, text = new OsuSpriteText());
+ FlowContent.Insert(-1, labelText = new OsuSpriteText());
+
+ updateDisabled();
}
- text.Text = value;
+ labelText.Text = value;
}
}
@@ -96,13 +98,19 @@ namespace osu.Game.Overlays.Settings
if (controlWithCurrent != null)
{
controlWithCurrent.Current.ValueChanged += _ => SettingChanged?.Invoke();
- controlWithCurrent.Current.DisabledChanged += disabled => { Colour = disabled ? Color4.Gray : Color4.White; };
+ controlWithCurrent.Current.DisabledChanged += _ => updateDisabled();
if (ShowsDefaultIndicator)
restoreDefaultButton.Bindable = controlWithCurrent.Current;
}
}
+ private void updateDisabled()
+ {
+ if (labelText != null)
+ labelText.Alpha = controlWithCurrent.Current.Disabled ? 0.3f : 1;
+ }
+
private class RestoreDefaultValueButton : Container, IHasTooltip
{
private Bindable bindable;
diff --git a/osu.Game/Overlays/Settings/SettingsSubsection.cs b/osu.Game/Overlays/Settings/SettingsSubsection.cs
index 9b3b2f570c..b096c146a6 100644
--- a/osu.Game/Overlays/Settings/SettingsSubsection.cs
+++ b/osu.Game/Overlays/Settings/SettingsSubsection.cs
@@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Settings
{
Text = Header.ToUpperInvariant(),
Margin = new MarginPadding { Bottom = 10, Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS },
- Font = OsuFont.GetFont(weight: FontWeight.Black),
+ Font = OsuFont.GetFont(weight: FontWeight.Bold),
},
FlowContent
});
diff --git a/osu.Game/Overlays/Social/SocialGridPanel.cs b/osu.Game/Overlays/Social/SocialGridPanel.cs
deleted file mode 100644
index 6f707d640b..0000000000
--- a/osu.Game/Overlays/Social/SocialGridPanel.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.Users;
-
-namespace osu.Game.Overlays.Social
-{
- public class SocialGridPanel : SocialPanel
- {
- public SocialGridPanel(User user)
- : base(user)
- {
- Width = 300;
- }
- }
-}
diff --git a/osu.Game/Overlays/Social/SocialListPanel.cs b/osu.Game/Overlays/Social/SocialListPanel.cs
deleted file mode 100644
index 1ba91e9204..0000000000
--- a/osu.Game/Overlays/Social/SocialListPanel.cs
+++ /dev/null
@@ -1,17 +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.Framework.Graphics;
-using osu.Game.Users;
-
-namespace osu.Game.Overlays.Social
-{
- public class SocialListPanel : SocialPanel
- {
- public SocialListPanel(User user)
- : base(user)
- {
- RelativeSizeAxes = Axes.X;
- }
- }
-}
diff --git a/osu.Game/Overlays/Social/SocialPanel.cs b/osu.Game/Overlays/Social/SocialPanel.cs
deleted file mode 100644
index 555527670a..0000000000
--- a/osu.Game/Overlays/Social/SocialPanel.cs
+++ /dev/null
@@ -1,61 +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 osuTK;
-using osuTK.Graphics;
-using osu.Framework.Extensions.Color4Extensions;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Effects;
-using osu.Framework.Input.Events;
-using osu.Game.Users;
-
-namespace osu.Game.Overlays.Social
-{
- public class SocialPanel : UserPanel
- {
- private const double hover_transition_time = 400;
-
- public SocialPanel(User user)
- : base(user)
- {
- }
-
- private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters
- {
- Type = EdgeEffectType.Shadow,
- Offset = new Vector2(0f, 1f),
- Radius = 2f,
- Colour = Color4.Black.Opacity(0.25f),
- };
-
- private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters
- {
- Type = EdgeEffectType.Shadow,
- Offset = new Vector2(0f, 5f),
- Radius = 10f,
- Colour = Color4.Black.Opacity(0.3f),
- };
-
- protected override bool OnHover(HoverEvent e)
- {
- Content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint);
- Content.MoveToY(-4, hover_transition_time, Easing.OutQuint);
-
- return base.OnHover(e);
- }
-
- protected override void OnHoverLost(HoverLostEvent e)
- {
- Content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint);
- Content.MoveToY(0, hover_transition_time, Easing.OutQuint);
-
- base.OnHoverLost(e);
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
- this.FadeInFromZero(200, Easing.Out);
- }
- }
-}
diff --git a/osu.Game/Overlays/SocialOverlay.cs b/osu.Game/Overlays/SocialOverlay.cs
index 50c05e1b54..02f7c9b0d3 100644
--- a/osu.Game/Overlays/SocialOverlay.cs
+++ b/osu.Game/Overlays/SocialOverlay.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Overlays
public class SocialOverlay : SearchableListOverlay
{
private readonly LoadingSpinner loading;
- private FillFlowContainer panels;
+ private FillFlowContainer panels;
protected override Color4 BackgroundColour => Color4Extensions.FromHex(@"60284b");
protected override Color4 TrianglesColourLight => Color4Extensions.FromHex(@"672b51");
@@ -158,7 +158,7 @@ namespace osu.Game.Overlays
if (Filter.DisplayStyleControl.Dropdown.Current.Value == SortDirection.Descending)
sortedUsers = sortedUsers.Reverse();
- var newPanels = new FillFlowContainer
+ var newPanels = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
@@ -166,20 +166,21 @@ namespace osu.Game.Overlays
Margin = new MarginPadding { Top = 10 },
ChildrenEnumerable = sortedUsers.Select(u =>
{
- SocialPanel panel;
+ UserPanel panel;
switch (Filter.DisplayStyleControl.DisplayStyle.Value)
{
case PanelDisplayStyle.Grid:
- panel = new SocialGridPanel(u)
+ panel = new UserGridPanel(u)
{
Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre
+ Origin = Anchor.TopCentre,
+ Width = 290,
};
break;
default:
- panel = new SocialListPanel(u);
+ panel = new UserListPanel(u);
break;
}
diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs
index 960585b968..7113acbbfb 100644
--- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs
+++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs
@@ -12,7 +12,6 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
-using osuTK.Graphics;
namespace osu.Game.Rulesets.Judgements
{
@@ -68,7 +67,7 @@ namespace osu.Game.Rulesets.Judgements
{
Text = Result.Type.GetDescription().ToUpperInvariant(),
Font = OsuFont.Numeric.With(size: 20),
- Colour = judgementColour(Result.Type),
+ Colour = colours.ForHitResult(Result.Type),
Scale = new Vector2(0.85f, 1),
}, confineMode: ConfineMode.NoScaling)
};
@@ -110,28 +109,5 @@ namespace osu.Game.Rulesets.Judgements
Expire(true);
}
-
- private Color4 judgementColour(HitResult judgement)
- {
- switch (judgement)
- {
- case HitResult.Perfect:
- case HitResult.Great:
- return colours.Blue;
-
- case HitResult.Ok:
- case HitResult.Good:
- return colours.Green;
-
- case HitResult.Meh:
- return colours.Yellow;
-
- case HitResult.Miss:
- return colours.Red;
-
- default:
- return Color4.White;
- }
- }
}
}
diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs
index cd08aee453..cf8128301c 100644
--- a/osu.Game/Rulesets/Mods/ModCinema.cs
+++ b/osu.Game/Rulesets/Mods/ModCinema.cs
@@ -39,7 +39,6 @@ namespace osu.Game.Rulesets.Mods
{
player.Background.EnableUserDim.Value = false;
- player.DimmableVideo.IgnoreUserSettings.Value = true;
player.DimmableStoryboard.IgnoreUserSettings.Value = true;
player.BreakOverlay.Hide();
diff --git a/osu.Game/Rulesets/Mods/ModRandom.cs b/osu.Game/Rulesets/Mods/ModRandom.cs
new file mode 100644
index 0000000000..da55ab3fbf
--- /dev/null
+++ b/osu.Game/Rulesets/Mods/ModRandom.cs
@@ -0,0 +1,17 @@
+// 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.Graphics.Sprites;
+using osu.Game.Graphics;
+
+namespace osu.Game.Rulesets.Mods
+{
+ public abstract class ModRandom : Mod
+ {
+ public override string Name => "Random";
+ public override string Acronym => "RD";
+ public override ModType Type => ModType.Conversion;
+ public override IconUsage? Icon => OsuIcon.Dice;
+ public override double ScoreMultiplier => 1;
+ }
+}
diff --git a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs
index c2947c0aca..d9aa615c6e 100644
--- a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs
+++ b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs
@@ -17,6 +17,12 @@ namespace osu.Game.Rulesets.Replays.Types
///