diff --git a/osu.Android.props b/osu.Android.props
index adc340a734..969eb205e0 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -1,5 +1,7 @@
+ Debug
+ AnyCPU
bin\$(Configuration)
4
2.0
@@ -35,7 +37,7 @@
None
True
prompt
- true
+ true
false
SdkOnly
False
@@ -49,7 +51,6 @@
osu.licenseheader
-
@@ -60,7 +61,7 @@
-
-
+
+
diff --git a/osu.Android.sln.DotSettings b/osu.Android.sln.DotSettings
index 3f5bd9d34d..5a97fc7518 100644
--- a/osu.Android.sln.DotSettings
+++ b/osu.Android.sln.DotSettings
@@ -1,4 +1,4 @@
-
+
True
True
True
@@ -167,6 +167,14 @@
WARNING
<?xml version="1.0" encoding="utf-16"?><Profile name="Code Cleanup (peppy)"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSUseVar><BehavourStyle>CAN_CHANGE_TO_EXPLICIT</BehavourStyle><LocalVariableStyle>ALWAYS_EXPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_EXPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSUpdateFileHeader>True</CSUpdateFileHeader><CSCodeStyleAttributes ArrangeTypeAccessModifier="False" ArrangeTypeMemberAccessModifier="False" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="False" ArrangeBraces="False" ArrangeAttributes="False" ArrangeArgumentsStyle="False" /><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CSArrangeQualifiers>True</CSArrangeQualifiers></Profile>
Code Cleanup (peppy)
+ Required
+ Required
+ Required
+ Explicit
+ ExpressionBody
+ ExpressionBody
+ True
+ NEXT_LINE
True
True
True
@@ -176,12 +184,22 @@
True
True
NEXT_LINE
+ 1
+ 1
+ NEXT_LINE
+ MULTILINE
NEXT_LINE
+ 1
+ 1
True
+ NEXT_LINE
NEVER
NEVER
+ True
False
+ True
NEVER
+ False
False
True
False
@@ -189,6 +207,7 @@
True
True
False
+ False
CHOP_IF_LONG
True
200
diff --git a/osu.Game.Rulesets.Catch.Tests/CatchLegacyModConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchLegacyModConversionTest.cs
new file mode 100644
index 0000000000..04e6dea376
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests/CatchLegacyModConversionTest.cs
@@ -0,0 +1,29 @@
+// 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 NUnit.Framework;
+using osu.Game.Beatmaps.Legacy;
+using osu.Game.Rulesets.Catch.Mods;
+using osu.Game.Tests.Beatmaps;
+
+namespace osu.Game.Rulesets.Catch.Tests
+{
+ [TestFixture]
+ public class CatchLegacyModConversionTest : LegacyModConversionTest
+ {
+ [TestCase(LegacyMods.Easy, new[] { typeof(CatchModEasy) })]
+ [TestCase(LegacyMods.HardRock | LegacyMods.DoubleTime, new[] { typeof(CatchModHardRock), typeof(CatchModDoubleTime) })]
+ [TestCase(LegacyMods.DoubleTime, new[] { typeof(CatchModDoubleTime) })]
+ [TestCase(LegacyMods.Nightcore, new[] { typeof(CatchModNightcore) })]
+ [TestCase(LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(CatchModNightcore) })]
+ [TestCase(LegacyMods.Flashlight | LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(CatchModFlashlight), typeof(CatchModNightcore) })]
+ [TestCase(LegacyMods.Perfect, new[] { typeof(CatchModPerfect) })]
+ [TestCase(LegacyMods.SuddenDeath, new[] { typeof(CatchModSuddenDeath) })]
+ [TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath, new[] { typeof(CatchModPerfect) })]
+ [TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath | LegacyMods.DoubleTime, new[] { typeof(CatchModDoubleTime), typeof(CatchModPerfect) })]
+ public new void Test(LegacyMods legacyMods, Type[] expectedMods) => base.Test(legacyMods, expectedMods);
+
+ protected override Ruleset CreateRuleset() => new CatchRuleset();
+ }
+}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs
index a603d96201..7b8c699f2c 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs
@@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
+ protected override bool Autoplay => true;
+
[Test]
public void TestHyperDash()
{
diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs
index 5428b4eeb8..71d68ace94 100644
--- a/osu.Game.Rulesets.Catch/CatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs
@@ -46,6 +46,11 @@ namespace osu.Game.Rulesets.Catch
else if (mods.HasFlag(LegacyMods.DoubleTime))
yield return new CatchModDoubleTime();
+ if (mods.HasFlag(LegacyMods.Perfect))
+ yield return new CatchModPerfect();
+ else if (mods.HasFlag(LegacyMods.SuddenDeath))
+ yield return new CatchModSuddenDeath();
+
if (mods.HasFlag(LegacyMods.Autoplay))
yield return new CatchModAutoplay();
@@ -67,14 +72,8 @@ namespace osu.Game.Rulesets.Catch
if (mods.HasFlag(LegacyMods.NoFail))
yield return new CatchModNoFail();
- if (mods.HasFlag(LegacyMods.Perfect))
- yield return new CatchModPerfect();
-
if (mods.HasFlag(LegacyMods.Relax))
yield return new CatchModRelax();
-
- if (mods.HasFlag(LegacyMods.SuddenDeath))
- yield return new CatchModSuddenDeath();
}
public override IEnumerable GetModsFor(ModType type)
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
index 00734810b3..dd4a58a5ef 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
@@ -50,6 +50,10 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
public Func CheckPosition;
+ public bool IsOnPlate;
+
+ public override bool RemoveWhenNotAlive => IsOnPlate;
+
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (CheckPosition == null) return;
@@ -71,11 +75,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
switch (state)
{
case ArmedState.Miss:
- this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out).Expire();
+ this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out);
break;
case ArmedState.Hit:
- this.FadeOut().Expire();
+ this.FadeOut();
break;
}
}
diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs
index 8dd00756f2..6c8515eb90 100644
--- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs
+++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs
@@ -27,6 +27,8 @@ namespace osu.Game.Rulesets.Catch.Replays
protected Replay Replay;
+ private CatchReplayFrame currentFrame;
+
public override Replay Generate()
{
// todo: add support for HT DT
@@ -35,9 +37,6 @@ namespace osu.Game.Rulesets.Catch.Replays
float lastPosition = 0.5f;
double lastTime = 0;
- // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
- Replay.Frames.Add(new CatchReplayFrame(-100000, lastPosition));
-
void moveToNext(CatchHitObject h)
{
float positionChange = Math.Abs(lastPosition - h.X);
@@ -58,18 +57,18 @@ namespace osu.Game.Rulesets.Catch.Replays
{
//we are already in the correct range.
lastTime = h.StartTime;
- Replay.Frames.Add(new CatchReplayFrame(h.StartTime, lastPosition));
+ addFrame(h.StartTime, lastPosition);
return;
}
if (impossibleJump)
{
- Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
+ addFrame(h.StartTime, h.X);
}
else if (h.HyperDash)
{
- Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition));
- Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
+ addFrame(h.StartTime - timeAvailable, lastPosition);
+ addFrame(h.StartTime, h.X);
}
else if (dashRequired)
{
@@ -81,16 +80,16 @@ namespace osu.Game.Rulesets.Catch.Replays
float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable);
//dash movement
- Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + 1, lastPosition, true));
- Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition));
- Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
+ addFrame(h.StartTime - timeAvailable + 1, lastPosition, true);
+ addFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition);
+ addFrame(h.StartTime, h.X);
}
else
{
double timeBefore = positionChange / movement_speed;
- Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition));
- Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
+ addFrame(h.StartTime - timeBefore, lastPosition);
+ addFrame(h.StartTime, h.X);
}
lastTime = h.StartTime;
@@ -122,5 +121,16 @@ namespace osu.Game.Rulesets.Catch.Replays
return Replay;
}
+
+ private void addFrame(double time, float? position = null, bool dashing = false)
+ {
+ // todo: can be removed once FramedReplayInputHandler correctly handles rewinding before first frame.
+ if (Replay.Frames.Count == 0)
+ Replay.Frames.Add(new CatchReplayFrame(time - 1, position, false, null));
+
+ var last = currentFrame;
+ currentFrame = new CatchReplayFrame(time, position, dashing, last);
+ Replay.Frames.Add(currentFrame);
+ }
}
}
diff --git a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs
index 103aa6c3f1..22532bc9ec 100644
--- a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs
+++ b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Diagnostics;
+using System.Linq;
using osu.Framework.Input.StateChanges;
using osu.Framework.MathUtils;
using osu.Game.Replays;
@@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Replays
{
}
- protected override bool IsImportant(CatchReplayFrame frame) => frame.Position > 0;
+ protected override bool IsImportant(CatchReplayFrame frame) => frame.Actions.Any();
protected float? Position
{
@@ -38,21 +39,11 @@ namespace osu.Game.Rulesets.Catch.Replays
{
if (!Position.HasValue) return new List();
- var actions = new List();
-
- if (CurrentFrame.Dashing)
- actions.Add(CatchAction.Dash);
-
- if (Position.Value > CurrentFrame.Position)
- actions.Add(CatchAction.MoveRight);
- else if (Position.Value < CurrentFrame.Position)
- actions.Add(CatchAction.MoveLeft);
-
return new List
{
new CatchReplayState
{
- PressedActions = actions,
+ PressedActions = CurrentFrame?.Actions ?? new List(),
CatcherX = Position.Value
},
};
diff --git a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs
index 1e88b35c3b..b41a5e0612 100644
--- a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs
+++ b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs
@@ -1,6 +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 System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Replays.Legacy;
using osu.Game.Rulesets.Catch.UI;
@@ -11,6 +12,8 @@ namespace osu.Game.Rulesets.Catch.Replays
{
public class CatchReplayFrame : ReplayFrame, IConvertibleReplayFrame
{
+ public List Actions = new List();
+
public float Position;
public bool Dashing;
@@ -18,17 +21,40 @@ namespace osu.Game.Rulesets.Catch.Replays
{
}
- public CatchReplayFrame(double time, float? position = null, bool dashing = false)
+ public CatchReplayFrame(double time, float? position = null, bool dashing = false, CatchReplayFrame lastFrame = null)
: base(time)
{
Position = position ?? -1;
Dashing = dashing;
+
+ if (Dashing)
+ Actions.Add(CatchAction.Dash);
+
+ if (lastFrame != null)
+ {
+ if (Position > lastFrame.Position)
+ lastFrame.Actions.Add(CatchAction.MoveRight);
+ else if (Position < lastFrame.Position)
+ lastFrame.Actions.Add(CatchAction.MoveLeft);
+ }
}
- public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap)
+ public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
- Position = legacyFrame.Position.X / CatchPlayfield.BASE_WIDTH;
- Dashing = legacyFrame.ButtonState == ReplayButtonState.Left1;
+ Position = currentFrame.Position.X / CatchPlayfield.BASE_WIDTH;
+ Dashing = currentFrame.ButtonState == ReplayButtonState.Left1;
+
+ if (Dashing)
+ Actions.Add(CatchAction.Dash);
+
+ // this probably needs some cross-checking with osu-stable to ensure it is actually correct.
+ if (lastFrame is CatchReplayFrame lastCatchFrame)
+ {
+ if (Position > lastCatchFrame.Position)
+ lastCatchFrame.Actions.Add(CatchAction.MoveRight);
+ else if (Position < lastCatchFrame.Position)
+ Actions.Add(CatchAction.MoveLeft);
+ }
}
}
}
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index ceda643335..56c8b33e02 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -69,10 +69,12 @@ namespace osu.Game.Rulesets.Catch.UI
caughtFruit.RelativePositionAxes = Axes.None;
caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0);
+ caughtFruit.IsOnPlate = true;
caughtFruit.Anchor = Anchor.TopCentre;
caughtFruit.Origin = Anchor.Centre;
caughtFruit.Scale *= 0.7f;
+ caughtFruit.LifetimeStart = caughtFruit.HitObject.StartTime;
caughtFruit.LifetimeEnd = double.MaxValue;
MovableCatcher.Add(caughtFruit);
@@ -205,7 +207,8 @@ namespace osu.Game.Rulesets.Catch.UI
AdditiveTarget.Add(additive);
- additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire();
+ additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint);
+ additive.Expire(true);
Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
}
@@ -300,6 +303,7 @@ namespace osu.Game.Rulesets.Catch.UI
{
this.FadeColour(Color4.White, hyper_dash_transition_length, Easing.OutQuint);
this.FadeTo(1, hyper_dash_transition_length, Easing.OutQuint);
+ Trail &= Dashing;
}
}
else
@@ -406,6 +410,9 @@ namespace osu.Game.Rulesets.Catch.UI
f.MoveToY(f.Y + 75, 750, Easing.InSine);
f.FadeOut(750);
+
+ // todo: this shouldn't exist once DrawableHitObject's ClearTransformsAfter overrides are repaired.
+ f.LifetimeStart = Time.Current;
f.Expire();
}
}
@@ -436,10 +443,13 @@ namespace osu.Game.Rulesets.Catch.UI
ExplodingFruitTarget.Add(fruit);
}
+ fruit.ClearTransforms();
fruit.MoveToY(fruit.Y - 50, 250, Easing.OutSine).Then().MoveToY(fruit.Y + 50, 500, Easing.InSine);
fruit.MoveToX(fruit.X + originalX * 6, 1000);
fruit.FadeOut(750);
+ // todo: this shouldn't exist once DrawableHitObject's ClearTransformsAfter overrides are repaired.
+ fruit.LifetimeStart = Time.Current;
fruit.Expire();
}
}
diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaLegacyModConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaLegacyModConversionTest.cs
new file mode 100644
index 0000000000..957743c5f1
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/ManiaLegacyModConversionTest.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;
+using NUnit.Framework;
+using osu.Game.Beatmaps.Legacy;
+using osu.Game.Rulesets.Mania.Mods;
+using osu.Game.Tests.Beatmaps;
+
+namespace osu.Game.Rulesets.Mania.Tests
+{
+ [TestFixture]
+ public class ManiaLegacyModConversionTest : LegacyModConversionTest
+ {
+ [TestCase(LegacyMods.Easy, new[] { typeof(ManiaModEasy) })]
+ [TestCase(LegacyMods.HardRock | LegacyMods.DoubleTime, new[] { typeof(ManiaModHardRock), typeof(ManiaModDoubleTime) })]
+ [TestCase(LegacyMods.DoubleTime, new[] { typeof(ManiaModDoubleTime) })]
+ [TestCase(LegacyMods.Nightcore, new[] { typeof(ManiaModNightcore) })]
+ [TestCase(LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(ManiaModNightcore) })]
+ [TestCase(LegacyMods.Flashlight | LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(ManiaModFlashlight), typeof(ManiaModNightcore) })]
+ [TestCase(LegacyMods.Perfect, new[] { typeof(ManiaModPerfect) })]
+ [TestCase(LegacyMods.SuddenDeath, new[] { typeof(ManiaModSuddenDeath) })]
+ [TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath, new[] { typeof(ManiaModPerfect) })]
+ [TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath | LegacyMods.DoubleTime, new[] { typeof(ManiaModDoubleTime), typeof(ManiaModPerfect) })]
+ [TestCase(LegacyMods.Random | LegacyMods.SuddenDeath, new[] { typeof(ManiaModRandom), typeof(ManiaModSuddenDeath) })]
+ public new void Test(LegacyMods legacyMods, Type[] expectedMods) => base.Test(legacyMods, expectedMods);
+
+ protected override Ruleset CreateRuleset() => new ManiaRuleset();
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs
index 20ac5eaa39..a5248c7712 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs
@@ -3,6 +3,7 @@
using System.Linq;
using NUnit.Framework;
+using osu.Framework.Testing;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Replays;
@@ -12,8 +13,14 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests
{
[TestFixture]
+ [HeadlessTest]
public class TestSceneAutoGeneration : OsuTestScene
{
+ ///
+ /// The number of frames which are generated at the start of a replay regardless of hitobject content.
+ ///
+ private const int frame_offset = 1;
+
[Test]
public void TestSingleNote()
{
@@ -26,11 +33,11 @@ namespace osu.Game.Rulesets.Mania.Tests
var generated = new ManiaAutoGenerator(beatmap).Generate();
- Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
- Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
- Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
- Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released");
+ Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames");
+ Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
+ Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Special1), "Special1 has not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Special1), "Special1 has not been released");
}
[Test]
@@ -47,11 +54,11 @@ namespace osu.Game.Rulesets.Mania.Tests
var generated = new ManiaAutoGenerator(beatmap).Generate();
- Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
- Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
- Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
- Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released");
+ Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames");
+ Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
+ Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Special1), "Special1 has not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Special1), "Special1 has not been released");
}
[Test]
@@ -67,11 +74,11 @@ namespace osu.Game.Rulesets.Mania.Tests
var generated = new ManiaAutoGenerator(beatmap).Generate();
- Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
- Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
- Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
- Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
+ Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames");
+ Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
+ Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
}
[Test]
@@ -89,11 +96,13 @@ namespace osu.Game.Rulesets.Mania.Tests
var generated = new ManiaAutoGenerator(beatmap).Generate();
- Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
- Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
- Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
- Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
+ Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames");
+
+ Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
+ Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
+
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
}
[Test]
@@ -110,15 +119,15 @@ namespace osu.Game.Rulesets.Mania.Tests
var generated = new ManiaAutoGenerator(beatmap).Generate();
- Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames");
- Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
- Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect first note release time");
- Assert.AreEqual(2000, generated.Frames[3].Time, "Incorrect second note hit time");
- Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
- Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released");
- Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released");
+ Assert.IsTrue(generated.Frames.Count == frame_offset + 4, "Replay must have 4 generated frames");
+ Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time");
+ Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect first note release time");
+ Assert.AreEqual(2000, generated.Frames[frame_offset + 2].Time, "Incorrect second note hit time");
+ Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 3].Time, "Incorrect second note release time");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1), "Key1 has not been released");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key2), "Key2 has not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 3], ManiaAction.Key2), "Key2 has not been released");
}
[Test]
@@ -137,16 +146,16 @@ namespace osu.Game.Rulesets.Mania.Tests
var generated = new ManiaAutoGenerator(beatmap).Generate();
- Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames");
- Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
- Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect first note release time");
- Assert.AreEqual(2000, generated.Frames[2].Time, "Incorrect second note hit time");
- Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
- Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
- Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key1), "Key1 has not been released");
- Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has been released");
- Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released");
+ Assert.IsTrue(generated.Frames.Count == frame_offset + 4, "Replay must have 4 generated frames");
+ Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time");
+ Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 2].Time, "Incorrect first note release time");
+ Assert.AreEqual(2000, generated.Frames[frame_offset + 1].Time, "Incorrect second note hit time");
+ Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 3].Time, "Incorrect second note release time");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key1), "Key1 has not been released");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key2), "Key2 has been released");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 3], ManiaAction.Key2), "Key2 has not been released");
}
[Test]
@@ -164,14 +173,14 @@ namespace osu.Game.Rulesets.Mania.Tests
var generated = new ManiaAutoGenerator(beatmap).Generate();
- Assert.IsTrue(generated.Frames.Count == 4, "Replay must have 4 frames");
- Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
- Assert.AreEqual(3000, generated.Frames[2].Time, "Incorrect second note press time + first note release time");
- Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect second note release time");
- Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released");
- Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key2), "Key2 has not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been released");
+ Assert.IsTrue(generated.Frames.Count == frame_offset + 3, "Replay must have 3 generated frames");
+ Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time");
+ Assert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect second note press time + first note release time");
+ Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 2].Time, "Incorrect second note release time");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1), "Key1 has not been released");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key2), "Key2 has not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key2), "Key2 has not been released");
}
private bool checkContains(ReplayFrame frame, params ManiaAction[] actions) => actions.All(action => ((ManiaReplayFrame)frame).Actions.Contains(action));
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs
new file mode 100644
index 0000000000..26a1b1b1ec
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs
@@ -0,0 +1,62 @@
+// 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.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Mania.UI;
+using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Tests.Visual;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Mania.Tests
+{
+ [TestFixture]
+ public class TestSceneHitExplosion : OsuTestScene
+ {
+ private ScrollingTestContainer scrolling;
+
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DrawableNote),
+ typeof(DrawableManiaHitObject),
+ };
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ Child = scrolling = new ScrollingTestContainer(ScrollingDirection.Down)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativePositionAxes = Axes.Y,
+ Y = -0.25f,
+ Size = new Vector2(Column.COLUMN_WIDTH, NotePiece.NOTE_HEIGHT),
+ };
+
+ int runcount = 0;
+
+ AddRepeatStep("explode", () =>
+ {
+ runcount++;
+
+ if (runcount % 15 > 12)
+ return;
+
+ scrolling.AddRange(new Drawable[]
+ {
+ new HitExplosion((runcount / 15) % 2 == 0 ? new Color4(94, 0, 57, 255) : new Color4(6, 84, 0, 255), runcount % 6 != 0)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ }
+ });
+ }, 100);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs
index 031abb08e2..8dae5e6d84 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestSceneNotes.cs
@@ -1,4 +1,4 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
@@ -11,6 +11,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
@@ -40,6 +41,7 @@ namespace osu.Game.Rulesets.Mania.Tests
{
Child = new FillFlowContainer
{
+ Clock = new FramedClock(new ManualClock()),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
@@ -62,7 +64,7 @@ namespace osu.Game.Rulesets.Mania.Tests
private Drawable createNoteDisplay(ScrollingDirection direction, int identifier, out DrawableNote hitObject)
{
- var note = new Note { StartTime = 999999999 };
+ var note = new Note { StartTime = 0 };
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
return new ScrollingTestContainer(direction)
@@ -77,7 +79,7 @@ namespace osu.Game.Rulesets.Mania.Tests
private Drawable createHoldNoteDisplay(ScrollingDirection direction, int identifier, out DrawableHoldNote hitObject)
{
- var note = new HoldNote { StartTime = 999999999, Duration = 5000 };
+ var note = new HoldNote { StartTime = 0, Duration = 5000 };
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
return new ScrollingTestContainer(direction)
@@ -133,7 +135,7 @@ namespace osu.Game.Rulesets.Mania.Tests
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
Width = 1.25f,
- Colour = Color4.Black.Opacity(0.5f)
+ Colour = Color4.Green.Opacity(0.5f)
},
content = new Container { RelativeSizeAxes = Axes.Both }
}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs
index 395e6daf0a..e7fd601abe 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs
@@ -15,6 +15,7 @@ using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Tests.Visual;
using osuTK;
@@ -114,8 +115,7 @@ namespace osu.Game.Rulesets.Mania.Tests
var obj = new BarLine
{
StartTime = Time.Current + 2000,
- ControlPoint = new TimingControlPoint(),
- BeatIndex = major ? 0 : 1
+ Major = major,
};
obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs
index b591f9da22..f5412dcfc5 100644
--- a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs
+++ b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
{
base.InitialiseDefaults();
- Set(ManiaRulesetSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0);
+ Set(ManiaRulesetSetting.ScrollTime, 1500.0, 50.0, 5000.0, 50.0);
Set(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
}
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index 0c4e7d4858..c74a292331 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -46,6 +46,11 @@ namespace osu.Game.Rulesets.Mania
else if (mods.HasFlag(LegacyMods.DoubleTime))
yield return new ManiaModDoubleTime();
+ if (mods.HasFlag(LegacyMods.Perfect))
+ yield return new ManiaModPerfect();
+ else if (mods.HasFlag(LegacyMods.SuddenDeath))
+ yield return new ManiaModSuddenDeath();
+
if (mods.HasFlag(LegacyMods.Autoplay))
yield return new ManiaModAutoplay();
@@ -97,14 +102,8 @@ namespace osu.Game.Rulesets.Mania
if (mods.HasFlag(LegacyMods.NoFail))
yield return new ManiaModNoFail();
- if (mods.HasFlag(LegacyMods.Perfect))
- yield return new ManiaModPerfect();
-
if (mods.HasFlag(LegacyMods.Random))
yield return new ManiaModRandom();
-
- if (mods.HasFlag(LegacyMods.SuddenDeath))
- yield return new ManiaModSuddenDeath();
}
public override IEnumerable GetModsFor(ModType type)
diff --git a/osu.Game.Rulesets.Mania/Objects/BarLine.cs b/osu.Game.Rulesets.Mania/Objects/BarLine.cs
deleted file mode 100644
index 4c644a8f09..0000000000
--- a/osu.Game.Rulesets.Mania/Objects/BarLine.cs
+++ /dev/null
@@ -1,21 +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.Beatmaps.ControlPoints;
-
-namespace osu.Game.Rulesets.Mania.Objects
-{
- public class BarLine : ManiaHitObject
- {
- ///
- /// The control point which this bar line is part of.
- ///
- public TimingControlPoint ControlPoint;
-
- ///
- /// The index of the beat which this bar line represents within the control point.
- /// This is a "major" bar line if % == 0.
- ///
- public int BeatIndex;
- }
-}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs
index e9c352c97e..be21610525 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs
@@ -4,6 +4,7 @@
using osuTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osuTK.Graphics;
@@ -13,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// Visualises a . Although this derives DrawableManiaHitObject,
/// this does not handle input/sound like a normal hit object.
///
- public class DrawableBarLine : DrawableManiaHitObject
+ public class DrawableBarLine : DrawableHitObject
{
///
/// Height of major bar line triangles.
@@ -40,9 +41,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Colour = new Color4(255, 204, 33, 255),
});
- bool isMajor = barLine.BeatIndex % (int)barLine.ControlPoint.TimeSignature == 0;
-
- if (isMajor)
+ if (barLine.Major)
{
AddInternal(new EquilateralTriangle
{
@@ -65,10 +64,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
});
}
- if (!isMajor && barLine.BeatIndex % 2 == 1)
+ if (!barLine.Major)
Alpha = 0.2f;
}
+ protected override void UpdateInitialTransforms()
+ {
+ }
+
protected override void UpdateStateTransforms(ArmedState state)
{
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
index e5b114ca81..5bfa07bd14 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
@@ -51,11 +51,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
switch (state)
{
case ArmedState.Miss:
- this.FadeOut(150, Easing.In).Expire();
+ this.FadeOut(150, Easing.In);
break;
case ArmedState.Hit:
- this.FadeOut(150, Easing.OutQuint).Expire();
+ this.FadeOut(150, Easing.OutQuint);
break;
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
index 2cd81104a3..31221c05ee 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
@@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
///
public class DrawableNote : DrawableManiaHitObject, IKeyBindingHandler
{
+ public const float CORNER_RADIUS = NotePiece.NOTE_HEIGHT / 2;
+
private readonly NotePiece headPiece;
public DrawableNote(Note hitObject)
@@ -38,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
- Colour = colour.NewValue.Lighten(1f).Opacity(0.6f),
+ Colour = colour.NewValue.Lighten(1f).Opacity(0.2f),
Radius = 10,
};
}, true);
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs
index bb33693783..4521af7dfb 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs
@@ -18,8 +18,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
///
internal class NotePiece : Container, IHasAccentColour
{
- public const float NOTE_HEIGHT = 10;
- private const float head_colour_height = 6;
+ public const float NOTE_HEIGHT = 12;
private readonly IBindable direction = new Bindable();
@@ -39,8 +38,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
colouredBox = new Box
{
RelativeSizeAxes = Axes.X,
- Height = head_colour_height,
- Alpha = 0.2f
+ Height = NOTE_HEIGHT / 2,
+ Alpha = 0.1f
}
};
}
diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
index 7b8bbc2095..2b336ca16d 100644
--- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
@@ -47,9 +47,6 @@ namespace osu.Game.Rulesets.Mania.Replays
public override Replay Generate()
{
- // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
- Replay.Frames.Add(new ManiaReplayFrame(-100000, 0));
-
var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time);
var actions = new List();
@@ -70,6 +67,10 @@ namespace osu.Game.Rulesets.Mania.Replays
}
}
+ // todo: can be removed once FramedReplayInputHandler correctly handles rewinding before first frame.
+ if (Replay.Frames.Count == 0)
+ Replay.Frames.Add(new ManiaReplayFrame(group.First().Time - 1));
+
Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray()));
}
diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs
index f7277d3669..70ba5cd938 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)
+ public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
// We don't need to fully convert, just create the converter
var converter = new ManiaBeatmapConverter(beatmap);
diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index 91dd236ab1..3d2a070b0f 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -1,4 +1,4 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// 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;
@@ -11,6 +11,8 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Mania.UI.Components;
using osu.Game.Rulesets.UI.Scrolling;
using osuTK;
@@ -19,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.UI
{
public class Column : ScrollingPlayfield, IKeyBindingHandler, IHasAccentColour
{
- private const float column_width = 45;
+ public const float COLUMN_WIDTH = 80;
private const float special_column_width = 70;
///
@@ -41,10 +43,7 @@ namespace osu.Game.Rulesets.Mania.UI
Index = index;
RelativeSizeAxes = Axes.Y;
- Width = column_width;
-
- Masking = true;
- CornerRadius = 5;
+ Width = COLUMN_WIDTH;
background = new ColumnBackground { RelativeSizeAxes = Axes.Both };
@@ -67,7 +66,7 @@ namespace osu.Game.Rulesets.Mania.UI
explosionContainer = new Container
{
Name = "Hit explosions",
- RelativeSizeAxes = Axes.Both
+ RelativeSizeAxes = Axes.Both,
}
}
},
@@ -90,6 +89,12 @@ namespace osu.Game.Rulesets.Mania.UI
Bottom = dir.NewValue == ScrollingDirection.Down ? ManiaStage.HIT_TARGET_POSITION : 0,
};
+ explosionContainer.Padding = new MarginPadding
+ {
+ Top = dir.NewValue == ScrollingDirection.Up ? NotePiece.NOTE_HEIGHT / 2 : 0,
+ Bottom = dir.NewValue == ScrollingDirection.Down ? NotePiece.NOTE_HEIGHT / 2 : 0
+ };
+
keyArea.Anchor = keyArea.Origin = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
}, true);
}
@@ -108,7 +113,7 @@ namespace osu.Game.Rulesets.Mania.UI
isSpecial = value;
- Width = isSpecial ? special_column_width : column_width;
+ Width = isSpecial ? special_column_width : COLUMN_WIDTH;
}
}
@@ -163,9 +168,10 @@ namespace osu.Game.Rulesets.Mania.UI
if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements.Value)
return;
- explosionContainer.Add(new HitExplosion(judgedObject)
+ explosionContainer.Add(new HitExplosion(judgedObject.AccentColour.Value, judgedObject is DrawableHoldNoteTick)
{
- Anchor = Direction.Value == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre
+ Anchor = Direction.Value == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre,
+ Origin = Anchor.Centre
});
}
diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs
index 5ee78aa496..57241da564 100644
--- a/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs
+++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs
@@ -35,7 +35,6 @@ namespace osu.Game.Rulesets.Mania.UI.Components
{
Name = "Background",
RelativeSizeAxes = Axes.Both,
- Alpha = 0.3f
},
backgroundOverlay = new Box
{
@@ -82,7 +81,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components
if (!IsLoaded)
return;
- background.Colour = AccentColour;
+ background.Colour = AccentColour.Darken(5);
var brightPoint = AccentColour.Opacity(0.6f);
var dimPoint = AccentColour.Opacity(0);
diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
index a0d713067d..386bcbb724 100644
--- a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
+++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
@@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
+using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osuTK.Graphics;
@@ -17,7 +18,6 @@ namespace osu.Game.Rulesets.Mania.UI.Components
{
public class ColumnHitObjectArea : CompositeDrawable, IHasAccentColour
{
- private const float hit_target_height = 10;
private const float hit_target_bar_height = 2;
private readonly IBindable direction = new Bindable();
@@ -32,7 +32,8 @@ namespace osu.Game.Rulesets.Mania.UI.Components
hitTargetBar = new Box
{
RelativeSizeAxes = Axes.X,
- Height = hit_target_height,
+ Height = NotePiece.NOTE_HEIGHT,
+ Alpha = 0.6f,
Colour = Color4.Black
},
hitTargetLine = new Container
diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
index f26526fe70..29863fba2e 100644
--- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
@@ -2,14 +2,11 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
-using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Input;
-using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
-using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Input.Handlers;
using osu.Game.Replays;
using osu.Game.Rulesets.Mania.Beatmaps;
@@ -19,8 +16,8 @@ using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
@@ -45,33 +42,7 @@ namespace osu.Game.Rulesets.Mania.UI
public DrawableManiaRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods)
: base(ruleset, beatmap, mods)
{
- // Generate the bar lines
- double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
-
- var timingPoints = Beatmap.ControlPointInfo.TimingPoints;
- var barLines = new List();
-
- for (int i = 0; i < timingPoints.Count; i++)
- {
- TimingControlPoint point = timingPoints[i];
-
- // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object
- double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature;
-
- int index = 0;
-
- for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
- {
- barLines.Add(new BarLine
- {
- StartTime = t,
- ControlPoint = point,
- BeatIndex = index
- });
- }
- }
-
- BarLines = barLines;
+ BarLines = new BarLineGenerator(Beatmap).BarLines;
}
[BackgroundDependencyLoader]
diff --git a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs
index 48470add8b..ccbff226a9 100644
--- a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs
+++ b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs
@@ -1,16 +1,14 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osuTK.Graphics;
+using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
-using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
-using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
-using osu.Game.Rulesets.Objects.Drawables;
using osuTK;
+using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.UI
{
@@ -18,51 +16,112 @@ namespace osu.Game.Rulesets.Mania.UI
{
public override bool RemoveWhenNotAlive => true;
- private readonly CircularContainer circle;
+ private readonly CircularContainer largeFaint;
+ private readonly CircularContainer mainGlow1;
- public HitExplosion(DrawableHitObject judgedObject)
+ public HitExplosion(Color4 objectColour, bool isSmall = false)
{
- bool isTick = judgedObject is DrawableHoldNoteTick;
-
- Origin = Anchor.Centre;
-
RelativeSizeAxes = Axes.X;
- Y = NotePiece.NOTE_HEIGHT / 2;
Height = NotePiece.NOTE_HEIGHT;
// scale roughly in-line with visual appearance of notes
- Scale = new Vector2(isTick ? 0.4f : 0.8f);
+ Scale = new Vector2(1f, 0.6f);
- InternalChild = circle = new CircularContainer
+ if (isSmall)
+ Scale *= 0.5f;
+
+ const float angle_variangle = 15; // should be less than 45
+
+ const float roundness = 80;
+
+ const float initial_height = 10;
+
+ var colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1);
+
+ InternalChildren = new Drawable[]
{
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Both,
- Masking = true,
- // we want our size to be very small so the glow dominates it.
- Size = new Vector2(0.1f),
- EdgeEffect = new EdgeEffectParameters
+ largeFaint = new CircularContainer
{
- Type = EdgeEffectType.Glow,
- Colour = Interpolation.ValueAt(0.1f, judgedObject.AccentColour.Value, Color4.White, 0, 1),
- Radius = 100,
- },
- Child = new Box
- {
- Alpha = 0,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
- AlwaysPresent = true
+ Masking = true,
+ // we want our size to be very small so the glow dominates it.
+ Size = new Vector2(0.8f),
+ Blending = BlendingParameters.Additive,
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Glow,
+ Colour = Interpolation.ValueAt(0.1f, objectColour, Color4.White, 0, 1).Opacity(0.3f),
+ Roundness = 160,
+ Radius = 200,
+ },
+ },
+ mainGlow1 = new CircularContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ Blending = BlendingParameters.Additive,
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Glow,
+ Colour = Interpolation.ValueAt(0.6f, objectColour, Color4.White, 0, 1),
+ Roundness = 20,
+ Radius = 50,
+ },
+ },
+ new CircularContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ Size = new Vector2(0.01f, initial_height),
+ Blending = BlendingParameters.Additive,
+ Rotation = RNG.NextSingle(-angle_variangle, angle_variangle),
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Glow,
+ Colour = colour,
+ Roundness = roundness,
+ Radius = 40,
+ },
+ },
+ new CircularContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ Size = new Vector2(0.01f, initial_height),
+ Blending = BlendingParameters.Additive,
+ Rotation = RNG.NextSingle(-angle_variangle, angle_variangle),
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Glow,
+ Colour = colour,
+ Roundness = roundness,
+ Radius = 40,
+ },
}
};
}
protected override void LoadComplete()
{
+ const double duration = 200;
+
base.LoadComplete();
- circle.ResizeTo(circle.Size * new Vector2(4, 20), 1000, Easing.OutQuint);
- this.FadeIn(16).Then().FadeOut(500, Easing.OutQuint);
+ largeFaint
+ .ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint)
+ .FadeOut(duration * 2);
+ mainGlow1.ScaleTo(1.4f, duration, Easing.OutQuint);
+
+ this.FadeOut(duration, Easing.Out);
Expire(true);
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
index 5ab07416a6..12faa499ad 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
@@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
using osuTK;
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
index a28de7ea58..98a4b7d0b6 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
@@ -12,6 +12,7 @@ using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
diff --git a/osu.Game.Rulesets.Osu.Tests/OsuLegacyModConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuLegacyModConversionTest.cs
new file mode 100644
index 0000000000..495f2738b5
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/OsuLegacyModConversionTest.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;
+using NUnit.Framework;
+using osu.Game.Beatmaps.Legacy;
+using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Tests.Beatmaps;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ [TestFixture]
+ public class OsuLegacyModConversionTest : LegacyModConversionTest
+ {
+ [TestCase(LegacyMods.Easy, new[] { typeof(OsuModEasy) })]
+ [TestCase(LegacyMods.HardRock | LegacyMods.DoubleTime, new[] { typeof(OsuModHardRock), typeof(OsuModDoubleTime) })]
+ [TestCase(LegacyMods.DoubleTime, new[] { typeof(OsuModDoubleTime) })]
+ [TestCase(LegacyMods.Nightcore, new[] { typeof(OsuModNightcore) })]
+ [TestCase(LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(OsuModNightcore) })]
+ [TestCase(LegacyMods.Flashlight | LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(OsuModFlashlight), typeof(OsuModFlashlight) })]
+ [TestCase(LegacyMods.Perfect, new[] { typeof(OsuModPerfect) })]
+ [TestCase(LegacyMods.SuddenDeath, new[] { typeof(OsuModSuddenDeath) })]
+ [TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath, new[] { typeof(OsuModPerfect) })]
+ [TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath | LegacyMods.DoubleTime, new[] { typeof(OsuModDoubleTime), typeof(OsuModPerfect) })]
+ [TestCase(LegacyMods.SpunOut | LegacyMods.Easy, new[] { typeof(OsuModSpunOut), typeof(OsuModEasy) })]
+ public new void Test(LegacyMods legacyMods, Type[] expectedMods) => base.Test(legacyMods, expectedMods);
+
+ protected override Ruleset CreateRuleset() => new OsuRuleset();
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/approachcircle@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/approachcircle@2x.png
deleted file mode 100755
index db2f4a5730..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/approachcircle@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/cursor@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/cursor@2x.png
deleted file mode 100755
index 75f9ba5ea6..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/cursor@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/cursormiddle@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/cursormiddle@2x.png
deleted file mode 100755
index ebf59c18ba..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/cursormiddle@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit0@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit0@2x.png
deleted file mode 100644
index bdb2bcbc41..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit0@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit100@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit100@2x.png
deleted file mode 100644
index 7db8eb3124..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit100@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit100k@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit100k@2x.png
deleted file mode 100644
index 206840e467..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit100k@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300@2x.png
deleted file mode 100644
index 2c7c07852f..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300g@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300g@2x.png
deleted file mode 100644
index 1ce746e3a4..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300g@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300k@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300k@2x.png
deleted file mode 100755
index b0db9c00af..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300k@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit50@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit50@2x.png
deleted file mode 100644
index 94c09d263a..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit50@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hitcircle@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hitcircle@2x.png
deleted file mode 100755
index 6674616472..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hitcircle@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hitcircleoverlay@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hitcircleoverlay@2x.png
deleted file mode 100755
index 1f98c1697e..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hitcircleoverlay@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb-nd@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb-nd@2x.png
deleted file mode 100644
index 626fd91e38..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb-nd@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb-spec@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb-spec@2x.png
deleted file mode 100644
index 76fd9ab168..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb-spec@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb0@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb0@2x.png
deleted file mode 100644
index 0a24a72808..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb0@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb1@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb1@2x.png
deleted file mode 100644
index e99f076947..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb1@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb2@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb2@2x.png
deleted file mode 100644
index cd36a0ae16..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb2@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb3@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb3@2x.png
deleted file mode 100644
index f494bd3f51..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb3@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb4@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb4@2x.png
deleted file mode 100644
index a5b19887d6..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb4@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb5@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb5@2x.png
deleted file mode 100644
index 4bb01f0e88..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb5@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb6@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb6@2x.png
deleted file mode 100644
index 859e0aa4c1..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb6@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb7@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb7@2x.png
deleted file mode 100644
index 90efda0994..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb7@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb8@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb8@2x.png
deleted file mode 100644
index fcdf4ed4a4..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb8@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb9@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb9@2x.png
deleted file mode 100644
index c990cf0fe6..0000000000
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/default-skin/sliderb9@2x.png and /dev/null differ
diff --git a/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs b/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs
index 29e5146ff1..38aac50df6 100644
--- a/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs
+++ b/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs
@@ -26,12 +26,12 @@ namespace osu.Game.Rulesets.Osu.Tests
}
[BackgroundDependencyLoader]
- private void load(AudioManager audio)
+ private void load(AudioManager audio, SkinManager skinManager)
{
var dllStore = new DllResourceStore("osu.Game.Rulesets.Osu.Tests.dll");
metricsSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore(dllStore, "Resources/metrics_skin"), audio, true);
- defaultSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore(dllStore, "Resources/default_skin"), audio, false);
+ defaultSkin = skinManager.GetSkin(DefaultLegacySkin.Info);
specialSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore(dllStore, "Resources/special_skin"), audio, true);
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs
new file mode 100644
index 0000000000..685a51d208
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs
@@ -0,0 +1,128 @@
+// 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 NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Audio.Sample;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Textures;
+using osu.Framework.Testing.Input;
+using osu.Game.Audio;
+using osu.Game.Rulesets.Osu.Skinning;
+using osu.Game.Rulesets.Osu.UI.Cursor;
+using osu.Game.Skinning;
+using osu.Game.Tests.Visual;
+using osuTK;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ public class TestSceneCursorTrail : OsuTestScene
+ {
+ [Test]
+ public void TestSmoothCursorTrail()
+ {
+ Container scalingContainer = null;
+
+ createTest(() => scalingContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = new CursorTrail()
+ });
+
+ AddStep("set large scale", () => scalingContainer.Scale = new Vector2(10));
+ }
+
+ [Test]
+ public void TestLegacySmoothCursorTrail()
+ {
+ createTest(() => new LegacySkinContainer(false)
+ {
+ Child = new LegacyCursorTrail()
+ });
+ }
+
+ [Test]
+ public void TestLegacyDisjointCursorTrail()
+ {
+ createTest(() => new LegacySkinContainer(true)
+ {
+ Child = new LegacyCursorTrail()
+ });
+ }
+
+ private void createTest(Func createContent) => AddStep("create trail", () =>
+ {
+ Clear();
+
+ Add(new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Size = new Vector2(0.8f),
+ Child = new MovingCursorInputManager { Child = createContent?.Invoke() }
+ });
+ });
+
+ [Cached(typeof(ISkinSource))]
+ private class LegacySkinContainer : Container, ISkinSource
+ {
+ private readonly bool disjoint;
+
+ public LegacySkinContainer(bool disjoint)
+ {
+ this.disjoint = disjoint;
+
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotImplementedException();
+
+ public Texture GetTexture(string componentName)
+ {
+ switch (componentName)
+ {
+ case "cursortrail":
+ var tex = new Texture(Texture.WhitePixel.TextureGL);
+
+ if (disjoint)
+ tex.ScaleAdjust = 1 / 25f;
+ return tex;
+
+ case "cursormiddle":
+ return disjoint ? null : Texture.WhitePixel;
+ }
+
+ return null;
+ }
+
+ public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
+
+ public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException();
+
+ public event Action SourceChanged;
+ }
+
+ private class MovingCursorInputManager : ManualInputManager
+ {
+ public MovingCursorInputManager()
+ {
+ UseParentInput = false;
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ const double spin_duration = 1000;
+ double currentTime = Time.Current;
+
+ double angle = (currentTime % spin_duration) / spin_duration * 2 * Math.PI;
+ Vector2 rPos = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
+
+ MoveMouseTo(ToScreenSpace(DrawSize / 2 + DrawSize / 3 * rPos));
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
index ebb6cd3a5a..aa170eae1e 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
@@ -6,23 +6,68 @@ using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
+using osu.Framework.Testing.Input;
using osu.Game.Rulesets.Osu.UI.Cursor;
+using osuTK;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
public class TestSceneGameplayCursor : SkinnableTestScene
{
- public override IReadOnlyList RequiredTypes => new[] { typeof(CursorTrail) };
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(OsuCursorContainer),
+ typeof(CursorTrail)
+ };
[BackgroundDependencyLoader]
private void load()
{
- SetContents(() => new OsuCursorContainer
+ SetContents(() => new MovingCursorInputManager
{
- RelativeSizeAxes = Axes.Both,
- Masking = true,
+ Child = new ClickingCursorContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ }
});
}
+
+ private class ClickingCursorContainer : OsuCursorContainer
+ {
+ protected override void Update()
+ {
+ base.Update();
+
+ double currentTime = Time.Current;
+
+ if (((int)(currentTime / 1000)) % 2 == 0)
+ OnPressed(OsuAction.LeftButton);
+ else
+ OnReleased(OsuAction.LeftButton);
+ }
+ }
+
+ private class MovingCursorInputManager : ManualInputManager
+ {
+ public MovingCursorInputManager()
+ {
+ UseParentInput = false;
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ const double spin_duration = 5000;
+ double currentTime = Time.Current;
+
+ double angle = (currentTime % spin_duration) / spin_duration * 2 * Math.PI;
+ Vector2 rPos = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
+
+ MoveMouseTo(ToScreenSpace(DrawSize / 2 + DrawSize / 3 * rPos));
+ }
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs
index 399cf22599..95c2810e94 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs
@@ -29,7 +29,8 @@ namespace osu.Game.Rulesets.Osu.Tests
};
for (int i = 0; i < 512; i++)
- beatmap.HitObjects.Add(new HitCircle { Position = new Vector2(256, 192), StartTime = i * 100 });
+ if (i % 32 < 20)
+ beatmap.HitObjects.Add(new HitCircle { Position = new Vector2(256, 192), StartTime = i * 100 });
return beatmap;
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index 985dcbca86..83646c561d 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -86,6 +86,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
AccentColour.BindValueChanged(accent => ApproachCircle.Colour = accent.NewValue, true);
}
+ public override double LifetimeStart
+ {
+ get => base.LifetimeStart;
+ set
+ {
+ base.LifetimeStart = value;
+ ApproachCircle.LifetimeStart = value;
+ }
+ }
+
+ public override double LifetimeEnd
+ {
+ get => base.LifetimeEnd;
+ set
+ {
+ base.LifetimeEnd = value;
+ ApproachCircle.LifetimeEnd = value;
+ }
+ }
+
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
Debug.Assert(HitObject.HitWindows != null);
@@ -122,6 +142,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateStateTransforms(ArmedState state)
{
+ base.UpdateStateTransforms(state);
+
Debug.Assert(HitObject.HitWindows != null);
switch (state)
@@ -132,22 +154,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Expire(true);
hitArea.HitAction = null;
-
- // override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early.
- LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.WindowFor(HitResult.Miss);
break;
case ArmedState.Miss:
ApproachCircle.FadeOut(50);
this.FadeOut(100);
- Expire();
break;
case ArmedState.Hit:
ApproachCircle.FadeOut(50);
// todo: temporary / arbitrary
- this.Delay(800).Expire();
+ this.Delay(800).FadeOut();
break;
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
index fcd42314fc..c46343c73c 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
@@ -41,6 +41,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength);
+ protected override void UpdateStateTransforms(ArmedState state)
+ {
+ base.UpdateStateTransforms(state);
+
+ switch (state)
+ {
+ case ArmedState.Idle:
+ // Manually set to reduce the number of future alive objects to a bare minimum.
+ LifetimeStart = HitObject.StartTime - HitObject.TimePreempt;
+ break;
+ }
+ }
+
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(HitObject, judgement);
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
index 00a943a67f..84d2a4af9b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
@@ -74,6 +74,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateStateTransforms(ArmedState state)
{
+ base.UpdateStateTransforms(state);
+
switch (state)
{
case ArmedState.Idle:
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 00c953c393..08b43b0345 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -202,6 +202,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateStateTransforms(ArmedState state)
{
+ base.UpdateStateTransforms(state);
+
Ball.FadeIn();
Ball.ScaleTo(HitObject.Scale);
@@ -219,10 +221,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
break;
}
- this.FadeOut(fade_out_time, Easing.OutQuint).Expire();
+ this.FadeOut(fade_out_time, Easing.OutQuint);
}
-
- Expire(true);
}
public Drawable ProxiedLayer => HeadCircle.ApproachCircle;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
index ba931976a8..9d4d9958a1 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
@@ -75,6 +75,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateStateTransforms(ArmedState state)
{
+ base.UpdateStateTransforms(state);
+
switch (state)
{
case ArmedState.Idle:
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index 49aaa2aaea..d1b9ee6cb4 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -215,14 +215,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateStateTransforms(ArmedState state)
{
+ base.UpdateStateTransforms(state);
+
var sequence = this.Delay(Spinner.Duration).FadeOut(160);
switch (state)
{
- case ArmedState.Idle:
- Expire(true);
- break;
-
case ArmedState.Hit:
sequence.ScaleTo(Scale * 1.2f, 320, Easing.Out);
break;
@@ -231,8 +229,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
sequence.ScaleTo(Scale * 0.8f, 320, Easing.In);
break;
}
-
- Expire();
}
}
}
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index ceb9ed9343..df2ae81a5a 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -52,6 +52,11 @@ namespace osu.Game.Rulesets.Osu
else if (mods.HasFlag(LegacyMods.DoubleTime))
yield return new OsuModDoubleTime();
+ if (mods.HasFlag(LegacyMods.Perfect))
+ yield return new OsuModPerfect();
+ else if (mods.HasFlag(LegacyMods.SuddenDeath))
+ yield return new OsuModSuddenDeath();
+
if (mods.HasFlag(LegacyMods.Autopilot))
yield return new OsuModAutopilot();
@@ -76,18 +81,12 @@ namespace osu.Game.Rulesets.Osu
if (mods.HasFlag(LegacyMods.NoFail))
yield return new OsuModNoFail();
- if (mods.HasFlag(LegacyMods.Perfect))
- yield return new OsuModPerfect();
-
if (mods.HasFlag(LegacyMods.Relax))
yield return new OsuModRelax();
if (mods.HasFlag(LegacyMods.SpunOut))
yield return new OsuModSpunOut();
- if (mods.HasFlag(LegacyMods.SuddenDeath))
- yield return new OsuModSuddenDeath();
-
if (mods.HasFlag(LegacyMods.Target))
yield return new OsuModTarget();
diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs
index 5971f053c2..8dd48eace0 100644
--- a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs
+++ b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs
@@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Osu
HitCircle,
FollowPoint,
Cursor,
+ CursorTrail,
SliderScorePoint,
ApproachCircle,
ReverseArrow,
diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs
index 4d90fcadd5..e6c6db5e61 100644
--- a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs
+++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs
@@ -26,11 +26,11 @@ namespace osu.Game.Rulesets.Osu.Replays
Actions.AddRange(actions);
}
- public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap)
+ public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
- Position = legacyFrame.Position;
- if (legacyFrame.MouseLeft) Actions.Add(OsuAction.LeftButton);
- if (legacyFrame.MouseRight) Actions.Add(OsuAction.RightButton);
+ Position = currentFrame.Position;
+ if (currentFrame.MouseLeft) Actions.Add(OsuAction.LeftButton);
+ if (currentFrame.MouseRight) Actions.Add(OsuAction.RightButton);
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs
new file mode 100644
index 0000000000..1885c76fcc
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs
@@ -0,0 +1,55 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Input.Events;
+using osu.Game.Rulesets.Osu.UI.Cursor;
+using osu.Game.Skinning;
+
+namespace osu.Game.Rulesets.Osu.Skinning
+{
+ public class LegacyCursorTrail : CursorTrail
+ {
+ private const double disjoint_trail_time_separation = 1000 / 60.0;
+
+ private bool disjointTrail;
+ private double lastTrailTime;
+
+ public LegacyCursorTrail()
+ {
+ Blending = BlendingParameters.Additive;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(ISkinSource skin)
+ {
+ Texture = skin.GetTexture("cursortrail");
+ disjointTrail = skin.GetTexture("cursormiddle") == null;
+
+ if (Texture != null)
+ {
+ // stable "magic ratio". see OsuPlayfieldAdjustmentContainer for full explanation.
+ Texture.ScaleAdjust *= 1.6f;
+ }
+ }
+
+ protected override double FadeDuration => disjointTrail ? 150 : 500;
+
+ protected override bool InterpolateMovements => !disjointTrail;
+
+ protected override bool OnMouseMove(MouseMoveEvent e)
+ {
+ if (!disjointTrail)
+ return base.OnMouseMove(e);
+
+ if (Time.Current - lastTrailTime >= disjoint_trail_time_separation)
+ {
+ lastTrailTime = Time.Current;
+ return base.OnMouseMove(e);
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs
index 5957b81d7e..479c250eab 100644
--- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs
@@ -45,6 +45,9 @@ namespace osu.Game.Rulesets.Osu.Skinning
switch (osuComponent.Component)
{
+ case OsuSkinComponents.FollowPoint:
+ return this.GetAnimation(component.LookupName, true, false);
+
case OsuSkinComponents.SliderFollowCircle:
return this.GetAnimation("sliderfollowcircle", true, true);
@@ -78,17 +81,23 @@ namespace osu.Game.Rulesets.Osu.Skinning
return null;
+ case OsuSkinComponents.CursorTrail:
+ if (source.GetTexture("cursortrail") != null)
+ return new LegacyCursorTrail();
+
+ return null;
+
case OsuSkinComponents.HitCircleText:
- var font = GetConfig(OsuSkinConfiguration.HitCircleFont)?.Value ?? "default";
+ var font = GetConfig(OsuSkinConfiguration.HitCirclePrefix)?.Value ?? "default";
var overlap = GetConfig(OsuSkinConfiguration.HitCircleOverlap)?.Value ?? 0;
return !hasFont(font)
? null
: new LegacySpriteText(source, font)
{
- // Spacing value was reverse-engineered from the ratio of the rendered sprite size in the visual inspector vs the actual texture size
- Scale = new Vector2(0.96f),
- Spacing = new Vector2(-overlap * 0.89f, 0)
+ // stable applies a blanket 0.8x scale to hitcircle fonts
+ Scale = new Vector2(0.8f),
+ Spacing = new Vector2(-overlap, 0)
};
}
diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs
index a6b87150ae..e7b686d27d 100644
--- a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs
@@ -5,7 +5,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
{
public enum OsuSkinConfiguration
{
- HitCircleFont,
+ HitCirclePrefix,
HitCircleOverlap,
SliderBorderSize,
SliderPathRadius,
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
index 05eb0ffdbf..b32dfd483f 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
@@ -5,6 +5,7 @@ using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using osu.Framework.Allocation;
+using osu.Framework.Caching;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Batches;
using osu.Framework.Graphics.OpenGL.Vertices;
@@ -20,30 +21,15 @@ using osuTK.Graphics.ES30;
namespace osu.Game.Rulesets.Osu.UI.Cursor
{
- internal class CursorTrail : Drawable, IRequireHighFrequencyMousePosition
+ public class CursorTrail : Drawable, IRequireHighFrequencyMousePosition
{
- private int currentIndex;
-
- private IShader shader;
- private Texture texture;
-
- private Vector2 size => texture.Size * Scale;
-
- private double timeOffset;
-
- private float time;
-
- public override bool IsPresent => true;
-
private const int max_sprites = 2048;
private readonly TrailPart[] parts = new TrailPart[max_sprites];
-
- private Vector2? lastPosition;
-
- private readonly InputResampler resampler = new InputResampler();
-
- protected override DrawNode CreateDrawNode() => new TrailDrawNode(this);
+ private int currentIndex;
+ private IShader shader;
+ private double timeOffset;
+ private float time;
public CursorTrail()
{
@@ -60,14 +46,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
}
}
- public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
-
[BackgroundDependencyLoader]
- private void load(ShaderManager shaders, TextureStore textures)
+ private void load(ShaderManager shaders)
{
shader = shaders.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE);
- texture = textures.Get(@"Cursor/cursortrail");
- Scale = new Vector2(1 / texture.ScaleAdjust);
}
protected override void LoadComplete()
@@ -76,6 +58,42 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
resetTime();
}
+ private Texture texture = Texture.WhitePixel;
+
+ public Texture Texture
+ {
+ get => texture;
+ set
+ {
+ if (texture == value)
+ return;
+
+ texture = value;
+ Invalidate(Invalidation.DrawNode);
+ }
+ }
+
+ private readonly Cached partSizeCache = new Cached();
+
+ private Vector2 partSize => partSizeCache.IsValid
+ ? partSizeCache.Value
+ : (partSizeCache.Value = new Vector2(Texture.DisplayWidth, Texture.DisplayHeight) * DrawInfo.Matrix.ExtractScale().Xy);
+
+ public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
+ {
+ if ((invalidation & (Invalidation.DrawInfo | Invalidation.RequiredParentSizeToFit | Invalidation.Presence)) > 0)
+ partSizeCache.Invalidate();
+
+ return base.Invalidate(invalidation, source, shallPropagate);
+ }
+
+ ///
+ /// The amount of time to fade the cursor trail pieces.
+ ///
+ protected virtual double FadeDuration => 300;
+
+ public override bool IsPresent => true;
+
protected override void Update()
{
base.Update();
@@ -84,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
const int fade_clock_reset_threshold = 1000000;
- time = (float)(Time.Current - timeOffset) / 300f;
+ time = (float)((Time.Current - timeOffset) / FadeDuration);
if (time > fade_clock_reset_threshold)
resetTime();
}
@@ -101,6 +119,16 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
timeOffset = Time.Current;
}
+ ///
+ /// Whether to interpolate mouse movements and add trail pieces at intermediate points.
+ ///
+ protected virtual bool InterpolateMovements => true;
+
+ private Vector2? lastPosition;
+ private readonly InputResampler resampler = new InputResampler();
+
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
+
protected override bool OnMouseMove(MouseMoveEvent e)
{
Vector2 pos = e.ScreenSpaceMousePosition;
@@ -116,33 +144,43 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
{
Trace.Assert(lastPosition.HasValue);
- // ReSharper disable once PossibleInvalidOperationException
- Vector2 pos1 = lastPosition.Value;
- Vector2 diff = pos2 - pos1;
- float distance = diff.Length;
- Vector2 direction = diff / distance;
-
- float interval = size.X / 2 * 0.9f;
-
- for (float d = interval; d < distance; d += interval)
+ if (InterpolateMovements)
{
- lastPosition = pos1 + direction * d;
- addPosition(lastPosition.Value);
+ // ReSharper disable once PossibleInvalidOperationException
+ Vector2 pos1 = lastPosition.Value;
+ Vector2 diff = pos2 - pos1;
+ float distance = diff.Length;
+ Vector2 direction = diff / distance;
+
+ float interval = partSize.X / 2.5f;
+
+ for (float d = interval; d < distance; d += interval)
+ {
+ lastPosition = pos1 + direction * d;
+ addPart(lastPosition.Value);
+ }
+ }
+ else
+ {
+ lastPosition = pos2;
+ addPart(lastPosition.Value);
}
}
return base.OnMouseMove(e);
}
- private void addPosition(Vector2 pos)
+ private void addPart(Vector2 screenSpacePosition)
{
- parts[currentIndex].Position = pos;
+ parts[currentIndex].Position = screenSpacePosition;
parts[currentIndex].Time = time;
++parts[currentIndex].InvalidationID;
currentIndex = (currentIndex + 1) % max_sprites;
}
+ protected override DrawNode CreateDrawNode() => new TrailDrawNode(this);
+
private struct TrailPart
{
public Vector2 Position;
@@ -177,7 +215,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
shader = Source.shader;
texture = Source.texture;
- size = Source.size;
+ size = Source.partSize;
time = Source.time;
for (int i = 0; i < Source.parts.Length; ++i)
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
index 893c7875fa..a944ff88c6 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
@@ -6,9 +6,12 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.UI;
+using osu.Game.Skinning;
+using osuTK;
namespace osu.Game.Rulesets.Osu.UI.Cursor
{
@@ -22,17 +25,14 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private readonly Bindable showTrail = new Bindable(true);
- private readonly CursorTrail cursorTrail;
+ private readonly Drawable cursorTrail;
public OsuCursorContainer()
{
InternalChild = fadeContainer = new Container
{
RelativeSizeAxes = Axes.Both,
- Children = new Drawable[]
- {
- cursorTrail = new CursorTrail { Depth = 1 }
- }
+ Child = cursorTrail = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling)
};
}
@@ -98,5 +98,15 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint);
ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint);
}
+
+ private class DefaultCursorTrail : CursorTrail
+ {
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ Texture = textures.Get(@"Cursor/cursortrail");
+ Scale = new Vector2(1 / Texture.ScaleAdjust);
+ }
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
index ea7eee8bb8..df12ebc514 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
@@ -70,13 +70,7 @@ namespace osu.Game.Rulesets.Osu.UI
base.Add(h);
}
- private void addApproachCircleProxy(Drawable d)
- {
- var proxy = d.CreateProxy();
- proxy.LifetimeStart = d.LifetimeStart;
- proxy.LifetimeEnd = d.LifetimeEnd;
- approachCircles.Add(proxy);
- }
+ private void addApproachCircleProxy(Drawable d) => approachCircles.Add(d.CreateProxy());
public override void PostProcess()
{
diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoLegacyModConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoLegacyModConversionTest.cs
new file mode 100644
index 0000000000..a59544386b
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko.Tests/TaikoLegacyModConversionTest.cs
@@ -0,0 +1,29 @@
+// 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 NUnit.Framework;
+using osu.Game.Beatmaps.Legacy;
+using osu.Game.Rulesets.Taiko.Mods;
+using osu.Game.Tests.Beatmaps;
+
+namespace osu.Game.Rulesets.Taiko.Tests
+{
+ [TestFixture]
+ public class TaikoLegacyModConversionTest : LegacyModConversionTest
+ {
+ [TestCase(LegacyMods.Easy, new[] { typeof(TaikoModEasy) })]
+ [TestCase(LegacyMods.HardRock | LegacyMods.DoubleTime, new[] { typeof(TaikoModHardRock), typeof(TaikoModDoubleTime) })]
+ [TestCase(LegacyMods.DoubleTime, new[] { typeof(TaikoModDoubleTime) })]
+ [TestCase(LegacyMods.Nightcore, new[] { typeof(TaikoModNightcore) })]
+ [TestCase(LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(TaikoModNightcore) })]
+ [TestCase(LegacyMods.Flashlight | LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(TaikoModFlashlight), typeof(TaikoModNightcore) })]
+ [TestCase(LegacyMods.Perfect, new[] { typeof(TaikoModPerfect) })]
+ [TestCase(LegacyMods.SuddenDeath, new[] { typeof(TaikoModSuddenDeath) })]
+ [TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath, new[] { typeof(TaikoModPerfect) })]
+ [TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath | LegacyMods.DoubleTime, new[] { typeof(TaikoModDoubleTime), typeof(TaikoModPerfect) })]
+ public new void Test(LegacyMods legacyMods, Type[] expectedMods) => base.Test(legacyMods, expectedMods);
+
+ protected override Ruleset CreateRuleset() => new TaikoRuleset();
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Objects/BarLine.cs b/osu.Game.Rulesets.Taiko/Objects/BarLine.cs
deleted file mode 100644
index a07012fd71..0000000000
--- a/osu.Game.Rulesets.Taiko/Objects/BarLine.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 BarLine : TaikoHitObject
- {
- }
-}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
index bf89f7e15b..1a5a797f28 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
@@ -3,6 +3,7 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Objects;
using osuTK;
using osu.Game.Rulesets.Objects.Drawables;
@@ -11,7 +12,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
///
/// A line that scrolls alongside hit objects in the playfield and visualises control points.
///
- public class DrawableBarLine : DrawableHitObject
+ public class DrawableBarLine : DrawableHitObject
{
///
/// The width of the line tracker.
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs
index 4d3a1a3f8a..f5b75a781b 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs
@@ -5,6 +5,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osuTK;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
index f4407a7b54..8e16a21199 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
@@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
case ArmedState.Hit:
case ArmedState.Miss:
- this.Delay(HitObject.Duration).FadeOut(100).Expire();
+ this.Delay(HitObject.Duration).FadeOut(100);
break;
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
index cef9a53deb..25b6141a0e 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
@@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
switch (state)
{
case ArmedState.Hit:
- this.ScaleTo(0, 100, Easing.OutQuint).Expire();
+ this.ScaleTo(0, 100, Easing.OutQuint);
break;
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
index 676ecd5a0b..4b25ff0ecc 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
@@ -105,12 +105,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
validActionPressed = false;
UnproxyContent();
- this.Delay(HitObject.HitWindows.WindowFor(HitResult.Miss)).Expire();
break;
case ArmedState.Miss:
- this.FadeOut(100)
- .Expire();
+ this.FadeOut(100);
break;
case ArmedState.Hit:
@@ -129,9 +127,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
.Then()
.MoveToY(gravity_travel_height * 2, gravity_time * 2, Easing.In);
- this.FadeOut(800)
- .Expire();
-
+ this.FadeOut(800);
break;
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
index 094ad1230f..07af7fe7e0 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
@@ -208,8 +208,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
this.FadeOut(transition_duration, Easing.Out);
bodyContainer.ScaleTo(1.4f, transition_duration);
-
- Expire();
}
break;
diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs
index 5203415e90..c5ebefc397 100644
--- a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs
+++ b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs
@@ -23,12 +23,12 @@ namespace osu.Game.Rulesets.Taiko.Replays
Actions.AddRange(actions);
}
- public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap)
+ public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
- if (legacyFrame.MouseRight1) Actions.Add(TaikoAction.LeftRim);
- if (legacyFrame.MouseRight2) Actions.Add(TaikoAction.RightRim);
- if (legacyFrame.MouseLeft1) Actions.Add(TaikoAction.LeftCentre);
- if (legacyFrame.MouseLeft2) Actions.Add(TaikoAction.RightCentre);
+ 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);
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
index 7fdb823388..b2655f592c 100644
--- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
@@ -45,6 +45,11 @@ namespace osu.Game.Rulesets.Taiko
else if (mods.HasFlag(LegacyMods.DoubleTime))
yield return new TaikoModDoubleTime();
+ if (mods.HasFlag(LegacyMods.Perfect))
+ yield return new TaikoModPerfect();
+ else if (mods.HasFlag(LegacyMods.SuddenDeath))
+ yield return new TaikoModSuddenDeath();
+
if (mods.HasFlag(LegacyMods.Autoplay))
yield return new TaikoModAutoplay();
@@ -66,14 +71,8 @@ namespace osu.Game.Rulesets.Taiko
if (mods.HasFlag(LegacyMods.NoFail))
yield return new TaikoModNoFail();
- if (mods.HasFlag(LegacyMods.Perfect))
- yield return new TaikoModPerfect();
-
if (mods.HasFlag(LegacyMods.Relax))
yield return new TaikoModRelax();
-
- if (mods.HasFlag(LegacyMods.SuddenDeath))
- yield return new TaikoModSuddenDeath();
}
public override IEnumerable GetModsFor(ModType type)
diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
index b03bea578e..5caa9e4626 100644
--- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
@@ -5,19 +5,18 @@ using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Taiko.Replays;
-using System.Linq;
using osu.Framework.Input;
using osu.Game.Configuration;
using osu.Game.Input.Handlers;
using osu.Game.Replays;
using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Taiko.UI
@@ -38,49 +37,7 @@ namespace osu.Game.Rulesets.Taiko.UI
[BackgroundDependencyLoader]
private void load()
{
- loadBarLines();
- }
-
- private void loadBarLines()
- {
- TaikoHitObject lastObject = Beatmap.HitObjects[Beatmap.HitObjects.Count - 1];
- double lastHitTime = 1 + ((lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime);
-
- var timingPoints = Beatmap.ControlPointInfo.TimingPoints.ToList();
-
- if (timingPoints.Count == 0)
- return;
-
- int currentIndex = 0;
- int currentBeat = 0;
- double time = timingPoints[currentIndex].Time;
-
- while (time <= lastHitTime)
- {
- int nextIndex = currentIndex + 1;
-
- if (nextIndex < timingPoints.Count && time > timingPoints[nextIndex].Time)
- {
- currentIndex = nextIndex;
- time = timingPoints[currentIndex].Time;
- currentBeat = 0;
- }
-
- var currentPoint = timingPoints[currentIndex];
-
- var barLine = new BarLine
- {
- StartTime = time,
- };
-
- barLine.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty);
-
- bool isMajor = currentBeat % (int)currentPoint.TimeSignature == 0;
- Playfield.Add(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine));
-
- time += currentPoint.BeatLength * (int)currentPoint.TimeSignature;
- currentBeat++;
- }
+ new BarLineGenerator(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar)));
}
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs
index 452ac859de..f94071a7a9 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs
@@ -3,6 +3,7 @@
using System.ComponentModel;
using System.Linq;
+using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play;
@@ -12,6 +13,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[Description("Player instantiated with an autoplay mod.")]
public class TestSceneAutoplay : AllPlayersTestScene
{
+ private ClockBackedTestWorkingBeatmap.TrackVirtualManual track;
+
protected override Player CreatePlayer(Ruleset ruleset)
{
Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
@@ -21,7 +24,18 @@ namespace osu.Game.Tests.Visual.Gameplay
protected override void AddCheckSteps()
{
AddUntilStep("score above zero", () => ((ScoreAccessiblePlayer)Player).ScoreProcessor.TotalScore.Value > 0);
- AddUntilStep("key counter counted keys", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 0));
+ AddUntilStep("key counter counted keys", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2));
+ AddStep("rewind", () => track.Seek(-10000));
+ AddUntilStep("key counter reset", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
+ }
+
+ protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap)
+ {
+ var working = base.CreateWorkingBeatmap(beatmap);
+
+ track = (ClockBackedTestWorkingBeatmap.TrackVirtualManual)working.Track;
+
+ return working;
}
private class ScoreAccessiblePlayer : TestPlayer
@@ -29,6 +43,8 @@ namespace osu.Game.Tests.Visual.Gameplay
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
+ public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
+
public ScoreAccessiblePlayer()
: base(false, false)
{
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs
index 237fee1594..ffc025a942 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs
@@ -14,6 +14,7 @@ using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play;
using osuTK;
@@ -47,9 +48,11 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("wait for track to start running", () => track.IsRunning);
addSeekStep(3000);
AddAssert("all judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged));
+ AddUntilStep("key counter counted keys", () => player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses >= 7));
AddStep("clear results", () => player.AppliedResults.Clear());
addSeekStep(0);
AddAssert("none judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged));
+ AddUntilStep("key counters reset", () => player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
AddAssert("no results triggered", () => player.AppliedResults.Count == 0);
}
@@ -90,6 +93,10 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public readonly List AppliedResults = new List();
+ public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
+
+ public new HUDOverlay HUDOverlay => base.HUDOverlay;
+
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
public new DrawableRuleset DrawableRuleset => base.DrawableRuleset;
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs
index 18088a9a5b..ad747e88e1 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs
@@ -7,7 +7,6 @@ using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.MathUtils;
-using osu.Framework.Timing;
using osu.Game.Screens.Play;
using osuTK.Input;
@@ -25,14 +24,15 @@ namespace osu.Game.Tests.Visual.Gameplay
public TestSceneKeyCounter()
{
- KeyCounterKeyboard rewindTestKeyCounterKeyboard;
+ KeyCounterKeyboard testCounter;
+
KeyCounterDisplay kc = new KeyCounterDisplay
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new KeyCounter[]
{
- rewindTestKeyCounterKeyboard = new KeyCounterKeyboard(Key.X),
+ testCounter = new KeyCounterKeyboard(Key.X),
new KeyCounterKeyboard(Key.X),
new KeyCounterMouse(MouseButton.Left),
new KeyCounterMouse(MouseButton.Right),
@@ -44,10 +44,8 @@ namespace osu.Game.Tests.Visual.Gameplay
Key key = (Key)((int)Key.A + RNG.Next(26));
kc.Add(new KeyCounterKeyboard(key));
});
- AddSliderStep("Fade time", 0, 200, 50, v => kc.FadeTime = v);
Key testKey = ((KeyCounterKeyboard)kc.Children.First()).Key;
- double time1 = 0;
AddStep($"Press {testKey} key", () =>
{
@@ -55,48 +53,17 @@ namespace osu.Game.Tests.Visual.Gameplay
InputManager.ReleaseKey(testKey);
});
- AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 1);
+ AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 1);
AddStep($"Press {testKey} key", () =>
{
InputManager.PressKey(testKey);
InputManager.ReleaseKey(testKey);
- time1 = Clock.CurrentTime;
});
- AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 2);
-
- IFrameBasedClock oldClock = null;
-
- AddStep($"Rewind {testKey} counter once", () =>
- {
- oldClock = rewindTestKeyCounterKeyboard.Clock;
- rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(time1 - 10));
- });
-
- AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 1);
-
- AddStep($"Rewind {testKey} counter to zero", () => rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(0)));
-
- AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 0);
-
- AddStep("Restore clock", () => rewindTestKeyCounterKeyboard.Clock = oldClock);
+ AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 2);
Add(kc);
}
-
- private class FixedClock : IClock
- {
- private readonly double time;
-
- public FixedClock(double time)
- {
- this.time = time;
- }
-
- public double CurrentTime => time;
- public double Rate => 1;
- public bool IsRunning => false;
- }
}
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs
index 5808a78056..50583e43c4 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs
@@ -160,6 +160,15 @@ namespace osu.Game.Tests.Visual.Gameplay
exitAndConfirm();
}
+ [Test]
+ public void TestRestartAfterResume()
+ {
+ pauseAndConfirm();
+ resumeAndConfirm();
+ restart();
+ confirmExited();
+ }
+
private void pauseAndConfirm()
{
pause();
@@ -198,6 +207,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("player exited", () => !Player.IsCurrentScreen());
}
+ private void restart() => AddStep("restart", () => Player.Restart());
private void pause() => AddStep("pause", () => Player.Pause());
private void resume() => AddStep("resume", () => Player.Resume());
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs b/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs
index 13116de320..681bf1b40b 100644
--- a/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs
+++ b/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Game.Online.API;
using osu.Game.Screens.Menu;
using osu.Game.Users;
@@ -11,17 +10,17 @@ namespace osu.Game.Tests.Visual.Menus
public class TestSceneDisclaimer : ScreenTestScene
{
[BackgroundDependencyLoader]
- private void load(IAPIProvider api)
+ private void load()
{
AddStep("load disclaimer", () => LoadScreen(new Disclaimer()));
AddStep("toggle support", () =>
{
- api.LocalUser.Value = new User
+ API.LocalUser.Value = new User
{
- Username = api.LocalUser.Value.Username,
- Id = api.LocalUser.Value.Id,
- IsSupporter = !api.LocalUser.Value.IsSupporter,
+ Username = API.LocalUser.Value.Username,
+ Id = API.LocalUser.Value.Id,
+ IsSupporter = !API.LocalUser.Value.IsSupporter,
};
});
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs
index 723e5fc03d..7ba1782a28 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneMatchLeaderboard : MultiplayerTestScene
{
- protected override bool RequiresAPIAccess => true;
+ protected override bool UseOnlineAPI => true;
public TestSceneMatchLeaderboard()
{
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs
index b646433846..dfe61a4dda 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[TestFixture]
public class TestSceneMultiScreen : ScreenTestScene
{
- protected override bool RequiresAPIAccess => true;
+ protected override bool UseOnlineAPI => true;
public override IReadOnlyList RequiredTypes => new[]
{
diff --git a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs
index 66ab1fe18a..31eab7f74e 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs
@@ -4,9 +4,9 @@
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Online.API;
using osu.Game.Overlays;
using osu.Game.Overlays.AccountCreation;
using osu.Game.Users;
@@ -27,6 +27,8 @@ namespace osu.Game.Tests.Visual.Online
private readonly Container userPanelArea;
+ private Bindable localUser;
+
public TestSceneAccountCreationOverlay()
{
AccountCreationOverlay accountCreation;
@@ -47,12 +49,14 @@ namespace osu.Game.Tests.Visual.Online
}
[BackgroundDependencyLoader]
- private void load(IAPIProvider api)
+ private void load()
{
- api.Logout();
- api.LocalUser.BindValueChanged(user => { userPanelArea.Child = new UserPanel(user.NewValue) { Width = 200 }; }, true);
+ API.Logout();
- AddStep("logout", api.Logout);
+ localUser = API.LocalUser.GetBoundCopy();
+ localUser.BindValueChanged(user => { userPanelArea.Child = new UserPanel(user.NewValue) { Width = 200 }; }, true);
+
+ AddStep("logout", API.Logout);
}
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
index 5068064a1f..9f03d947b9 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
@@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Online
typeof(BeatmapAvailability),
};
- protected override bool RequiresAPIAccess => true;
+ protected override bool UseOnlineAPI => true;
private RulesetInfo taikoRuleset;
private RulesetInfo maniaRuleset;
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
index 324291c9d7..f555c276f4 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Online
typeof(Comments),
};
- protected override bool RequiresAPIAccess => true;
+ protected override bool UseOnlineAPI => true;
protected override void LoadComplete()
{
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs
new file mode 100644
index 0000000000..4773e84a5e
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs
@@ -0,0 +1,108 @@
+// 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.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics.Containers;
+using osu.Game.Online.Chat;
+using osu.Game.Overlays.Chat;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ [TestFixture]
+ public class TestSceneChatLineTruncation : OsuTestScene
+ {
+ private readonly TestChatLineContainer textContainer;
+
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(ChatLine),
+ typeof(Message),
+ typeof(LinkFlowContainer),
+ typeof(MessageFormatter)
+ };
+
+ public TestSceneChatLineTruncation()
+ {
+ Add(textContainer = new TestChatLineContainer
+ {
+ Padding = new MarginPadding { Left = 20, Right = 20 },
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ });
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ testFormatting();
+ }
+
+ private void clear() => AddStep("clear messages", textContainer.Clear);
+
+ private void addMessageWithChecks(string text, bool isAction = false, bool isImportant = false, string username = null)
+ {
+ int index = textContainer.Count + 1;
+ var newLine = new ChatLine(new DummyMessage(text, isAction, isImportant, index, username));
+ textContainer.Add(newLine);
+ }
+
+ private void testFormatting()
+ {
+ for (int a = 0; a < 25; a++)
+ addMessageWithChecks($"Wide {a} character username.", username: new string('w', a));
+ addMessageWithChecks("Short name with spaces.", username: "sho rt name");
+ addMessageWithChecks("Long name with spaces.", username: "long name with s p a c e s");
+ }
+
+ private class DummyMessage : Message
+ {
+ private static long messageCounter;
+
+ internal static readonly User TEST_SENDER_BACKGROUND = new User
+ {
+ Username = @"i-am-important",
+ Id = 42,
+ Colour = "#250cc9",
+ };
+
+ internal static readonly User TEST_SENDER = new User
+ {
+ Username = @"Somebody",
+ Id = 1,
+ };
+
+ public new DateTimeOffset Timestamp = DateTimeOffset.Now;
+
+ public DummyMessage(string text, bool isAction = false, bool isImportant = false, int number = 0, string username = null)
+ : base(messageCounter++)
+ {
+ Content = text;
+ IsAction = isAction;
+ Sender = new User
+ {
+ Username = username ?? $"user {number}",
+ Id = number,
+ Colour = isImportant ? "#250cc9" : null,
+ };
+ }
+ }
+
+ private class TestChatLineContainer : FillFlowContainer
+ {
+ protected override int Compare(Drawable x, Drawable y)
+ {
+ var xC = (ChatLine)x;
+ var yC = (ChatLine)y;
+
+ return xC.Message.CompareTo(yC.Message);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs
index 14ae975806..d9873ea243 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual.Online
{
private DirectOverlay direct;
- protected override bool RequiresAPIAccess => true;
+ protected override bool UseOnlineAPI => true;
protected override void LoadComplete()
{
diff --git a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs
index c98f98c23d..d3b037f499 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs
@@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
public class TestSceneHistoricalSection : OsuTestScene
{
- protected override bool RequiresAPIAccess => true;
+ protected override bool UseOnlineAPI => true;
public override IReadOnlyList RequiredTypes => new[]
{
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsDismissableFlag.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsDismissableFlag.cs
new file mode 100644
index 0000000000..db6afa9bf3
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsDismissableFlag.cs
@@ -0,0 +1,65 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Graphics;
+using osu.Game.Overlays.Rankings;
+using osu.Game.Users;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneRankingsDismissableFlag : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DismissableFlag),
+ };
+
+ public TestSceneRankingsDismissableFlag()
+ {
+ DismissableFlag flag;
+ SpriteText text;
+
+ var countryA = new Country
+ {
+ FlagName = "BY",
+ FullName = "Belarus"
+ };
+
+ var countryB = new Country
+ {
+ FlagName = "US",
+ FullName = "United States"
+ };
+
+ AddRange(new Drawable[]
+ {
+ flag = new DismissableFlag
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(30, 20),
+ Country = countryA,
+ },
+ text = new SpriteText
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Text = "Invoked",
+ Font = OsuFont.GetFont(size: 30),
+ Alpha = 0,
+ }
+ });
+
+ flag.Action += () => text.FadeIn().Then().FadeOut(1000, Easing.OutQuint);
+
+ AddStep("Trigger click", () => flag.Click());
+ AddStep("Change to country 2", () => flag.Country = countryB);
+ AddStep("Change to country 1", () => flag.Country = countryA);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs
new file mode 100644
index 0000000000..c0da605cdb
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs
@@ -0,0 +1,77 @@
+// 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.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Overlays.Rankings;
+using osu.Game.Rulesets;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneRankingsHeader : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DismissableFlag),
+ typeof(HeaderTitle),
+ typeof(RankingsRulesetSelector),
+ typeof(RankingsScopeSelector),
+ typeof(RankingsHeader),
+ };
+
+ public TestSceneRankingsHeader()
+ {
+ var countryBindable = new Bindable();
+ var ruleset = new Bindable();
+ var scope = new Bindable();
+
+ Add(new RankingsHeader
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Scope = { BindTarget = scope },
+ Country = { BindTarget = countryBindable },
+ Ruleset = { BindTarget = ruleset },
+ Spotlights = new[]
+ {
+ new Spotlight
+ {
+ Id = 1,
+ Text = "Spotlight 1"
+ },
+ new Spotlight
+ {
+ Id = 2,
+ Text = "Spotlight 2"
+ },
+ new Spotlight
+ {
+ Id = 3,
+ Text = "Spotlight 3"
+ }
+ }
+ });
+
+ var country = new Country
+ {
+ FlagName = "BY",
+ FullName = "Belarus"
+ };
+
+ var unknownCountry = new Country
+ {
+ FlagName = "CK",
+ FullName = "Cook Islands"
+ };
+
+ AddStep("Set country", () => countryBindable.Value = country);
+ AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance);
+ AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
+ AddAssert("Check country is Null", () => countryBindable.Value == null);
+ AddStep("Set country with no flag", () => countryBindable.Value = unknownCountry);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs
new file mode 100644
index 0000000000..849ca2defc
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs
@@ -0,0 +1,60 @@
+// 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.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Overlays.Rankings;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneRankingsHeaderTitle : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DismissableFlag),
+ typeof(HeaderTitle),
+ };
+
+ public TestSceneRankingsHeaderTitle()
+ {
+ var countryBindable = new Bindable();
+ var scope = new Bindable();
+
+ Add(new HeaderTitle
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Country = { BindTarget = countryBindable },
+ Scope = { BindTarget = scope },
+ });
+
+ var countryA = new Country
+ {
+ FlagName = "BY",
+ FullName = "Belarus"
+ };
+
+ var countryB = new Country
+ {
+ FlagName = "US",
+ FullName = "United States"
+ };
+
+ AddStep("Set country", () => countryBindable.Value = countryA);
+ AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance);
+ AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
+ AddAssert("Check country is Null", () => countryBindable.Value == null);
+
+ AddStep("Set country 1", () => countryBindable.Value = countryA);
+ AddStep("Set country 2", () => countryBindable.Value = countryB);
+ AddStep("Set null country", () => countryBindable.Value = null);
+ AddStep("Set scope to Performance", () => scope.Value = RankingsScope.Performance);
+ AddStep("Set scope to Spotlights", () => scope.Value = RankingsScope.Spotlights);
+ AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
+ AddStep("Set scope to Country", () => scope.Value = RankingsScope.Country);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsRulesetSelector.cs
new file mode 100644
index 0000000000..84515bd3a4
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsRulesetSelector.cs
@@ -0,0 +1,41 @@
+// 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.Game.Overlays.Rankings;
+using osu.Framework.Graphics;
+using osu.Game.Rulesets;
+using osu.Framework.Bindables;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Mania;
+using osu.Game.Rulesets.Taiko;
+using osu.Game.Rulesets.Catch;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneRankingsRulesetSelector : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(RankingsRulesetSelector),
+ };
+
+ public TestSceneRankingsRulesetSelector()
+ {
+ var current = new Bindable();
+
+ Add(new RankingsRulesetSelector
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Current = { BindTarget = current }
+ });
+
+ AddStep("Select osu!", () => current.Value = new OsuRuleset().RulesetInfo);
+ AddStep("Select mania", () => current.Value = new ManiaRuleset().RulesetInfo);
+ AddStep("Select taiko", () => current.Value = new TaikoRuleset().RulesetInfo);
+ AddStep("Select catch", () => current.Value = new CatchRuleset().RulesetInfo);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs
new file mode 100644
index 0000000000..3693d6b5b4
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs
@@ -0,0 +1,54 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using osu.Framework.Graphics;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Allocation;
+using osu.Game.Graphics;
+using osu.Game.Overlays.Rankings;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneRankingsScopeSelector : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(RankingsScopeSelector),
+ };
+
+ private readonly Box background;
+
+ public TestSceneRankingsScopeSelector()
+ {
+ var scope = new Bindable();
+
+ AddRange(new Drawable[]
+ {
+ background = new Box
+ {
+ RelativeSizeAxes = Axes.Both
+ },
+ new RankingsScopeSelector
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Current = scope,
+ }
+ });
+
+ AddStep(@"Select country", () => scope.Value = RankingsScope.Country);
+ AddStep(@"Select performance", () => scope.Value = RankingsScope.Performance);
+ AddStep(@"Select score", () => scope.Value = RankingsScope.Score);
+ AddStep(@"Select spotlights", () => scope.Value = RankingsScope.Spotlights);
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ background.Colour = colours.GreySeafoam;
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs
index 806b36e855..dbd7544b38 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
public class TestSceneSocialOverlay : OsuTestScene
{
- protected override bool RequiresAPIAccess => true;
+ protected override bool UseOnlineAPI => true;
public override IReadOnlyList RequiredTypes => new[]
{
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs
index 555d5334d8..63b8acb234 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs
@@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Online
{
public class TestSceneUserProfileHeader : OsuTestScene
{
- protected override bool RequiresAPIAccess => true;
+ protected override bool UseOnlineAPI => true;
public override IReadOnlyList RequiredTypes => new[]
{
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs
index 42c8ffbf0a..93e6607ac5 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
public class TestSceneUserProfileOverlay : OsuTestScene
{
- protected override bool RequiresAPIAccess => true;
+ protected override bool UseOnlineAPI => true;
private readonly TestUserProfileOverlay profile;
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs
index d777f9766a..2951f6b63e 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
public class TestSceneUserRanks : OsuTestScene
{
- protected override bool RequiresAPIAccess => true;
+ protected override bool UseOnlineAPI => true;
public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableProfileScore), typeof(RanksSection) };
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs
index 7b97a27732..ed9e01a67e 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs
@@ -9,6 +9,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Screens.Select;
+using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual.SongSelect
@@ -30,45 +31,44 @@ namespace osu.Game.Tests.Visual.SongSelect
Size = new Vector2(550f, 450f),
});
- AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
+ AddStep("all metrics", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
+ {
+ BeatmapInfo =
{
- BeatmapSetInfo =
+ BeatmapSet = new BeatmapSetInfo
{
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
},
- BeatmapInfo =
+ Version = "All Metrics",
+ Metadata = new BeatmapMetadata
{
- Version = "All Metrics",
- Metadata = new BeatmapMetadata
- {
- Source = "osu!lazer",
- Tags = "this beatmap has all the metrics",
- },
- BaseDifficulty = new BeatmapDifficulty
- {
- CircleSize = 7,
- DrainRate = 1,
- OverallDifficulty = 5.7f,
- ApproachRate = 3.5f,
- },
- StarDifficulty = 5.3f,
- Metrics = new BeatmapMetrics
- {
- Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
- Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
- },
- }
+ Source = "osu!lazer",
+ Tags = "this beatmap has all the metrics",
+ },
+ BaseDifficulty = new BeatmapDifficulty
+ {
+ CircleSize = 7,
+ DrainRate = 1,
+ OverallDifficulty = 5.7f,
+ ApproachRate = 3.5f,
+ },
+ StarDifficulty = 5.3f,
+ Metrics = new BeatmapMetrics
+ {
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
+ },
}
- );
+ }));
- AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
+ AddStep("all except source", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
{
- BeatmapSetInfo =
- {
- Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
- },
BeatmapInfo =
{
+ BeatmapSet = new BeatmapSetInfo
+ {
+ Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
+ },
Version = "All Metrics",
Metadata = new BeatmapMetadata
{
@@ -88,16 +88,16 @@ namespace osu.Game.Tests.Visual.SongSelect
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
}
- });
+ }));
- AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
+ AddStep("ratings", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
{
- BeatmapSetInfo =
- {
- Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
- },
BeatmapInfo =
{
+ BeatmapSet = new BeatmapSetInfo
+ {
+ Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
+ },
Version = "Only Ratings",
Metadata = new BeatmapMetadata
{
@@ -113,9 +113,9 @@ namespace osu.Game.Tests.Visual.SongSelect
},
StarDifficulty = 4.8f
}
- });
+ }));
- AddStep("fails+retries", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
+ AddStep("fails+retries", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
{
BeatmapInfo =
{
@@ -139,9 +139,9 @@ namespace osu.Game.Tests.Visual.SongSelect
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
}
- });
+ }));
- AddStep("null metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
+ AddStep("null metrics", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
{
BeatmapInfo =
{
@@ -160,7 +160,7 @@ namespace osu.Game.Tests.Visual.SongSelect
},
StarDifficulty = 1.97f,
}
- });
+ }));
AddStep("null beatmap", () => detailsArea.Beatmap = null);
}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs
index 8e358a77db..186f27a8b2 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboard.cs
@@ -42,6 +42,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter));
AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn));
AddStep(@"Unavailable", () => leaderboard.SetRetrievalState(PlaceholderState.Unavailable));
+ AddStep(@"None selected", () => leaderboard.SetRetrievalState(PlaceholderState.NoneSelected));
foreach (BeatmapSetOnlineStatus status in Enum.GetValues(typeof(BeatmapSetOnlineStatus)))
AddStep($"{status} beatmap", () => showBeatmapWithStatus(status));
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs
index a6ff3462d4..cc4a57fb83 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs
@@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Add(overlay = new DialogOverlay());
- AddStep("dialog #1", () => overlay.Push(new PopupDialog
+ AddStep("dialog #1", () => overlay.Push(new TestPopupDialog
{
Icon = FontAwesome.Regular.TrashAlt,
HeaderText = @"Confirm deletion of",
@@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.UserInterface
},
}));
- AddStep("dialog #2", () => overlay.Push(new PopupDialog
+ AddStep("dialog #2", () => overlay.Push(new TestPopupDialog
{
Icon = FontAwesome.Solid.Cog,
HeaderText = @"What do you want to do with",
@@ -71,5 +71,9 @@ namespace osu.Game.Tests.Visual.UserInterface
},
}));
}
+
+ private class TestPopupDialog : PopupDialog
+ {
+ }
}
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs
index 9ddd8f4038..3d39bb7003 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs
@@ -13,13 +13,22 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public TestScenePopupDialog()
{
- var popup = new PopupDialog
+ Add(new TestPopupDialog
{
RelativeSizeAxes = Axes.Both,
State = { Value = Framework.Graphics.Containers.Visibility.Visible },
- Icon = FontAwesome.Solid.AssistiveListeningSystems,
- HeaderText = @"This is a test popup",
- BodyText = "I can say lots of stuff and even wrap my words!",
+ });
+ }
+
+ private class TestPopupDialog : PopupDialog
+ {
+ public TestPopupDialog()
+ {
+ Icon = FontAwesome.Solid.AssistiveListeningSystems;
+
+ HeaderText = @"This is a test popup";
+ BodyText = "I can say lots of stuff and even wrap my words!";
+
Buttons = new PopupDialogButton[]
{
new PopupDialogCancelButton
@@ -30,10 +39,8 @@ namespace osu.Game.Tests.Visual.UserInterface
{
Text = @"You're a fake!",
},
- }
- };
-
- Add(popup);
+ };
+ }
}
}
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs
index fdc50be3fa..198cc70e01 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneUpdateableBeatmapBackgroundSprite : OsuTestScene
{
- protected override bool RequiresAPIAccess => true;
+ protected override bool UseOnlineAPI => true;
private BeatmapSetInfo testBeatmap;
private IAPIProvider api;
diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs
index 3e0df8d45e..db9576b5fa 100644
--- a/osu.Game.Tests/WaveformTestBeatmap.cs
+++ b/osu.Game.Tests/WaveformTestBeatmap.cs
@@ -6,6 +6,7 @@ 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.Archives;
@@ -42,6 +43,8 @@ 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/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 166ba5111c..b9ed3664ef 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -13,6 +13,7 @@ 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;
@@ -340,6 +341,7 @@ 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 5bbffc2f77..1d00c94ef2 100644
--- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
@@ -7,6 +7,7 @@ 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;
@@ -64,6 +65,21 @@ namespace osu.Game.Beatmaps
}
}
+ protected override VideoSprite GetVideo()
+ {
+ if (Metadata?.VideoFile == null)
+ return null;
+
+ try
+ {
+ return new VideoSprite(textureStore.GetStream(getPathForFile(Metadata.VideoFile)));
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
protected override Track GetTrack()
{
try
diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs
index 001f319307..9267527d79 100644
--- a/osu.Game/Beatmaps/BeatmapMetadata.cs
+++ b/osu.Game/Beatmaps/BeatmapMetadata.cs
@@ -52,6 +52,7 @@ 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})";
@@ -81,7 +82,8 @@ namespace osu.Game.Beatmaps
&& Tags == other.Tags
&& PreviewTime == other.PreviewTime
&& AudioFile == other.AudioFile
- && BackgroundFile == other.BackgroundFile;
+ && BackgroundFile == other.BackgroundFile
+ && VideoFile == other.VideoFile;
}
}
}
diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
index 29ade24328..a3ab01c886 100644
--- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
@@ -7,6 +7,7 @@ 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;
@@ -44,6 +45,8 @@ 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 02d969b571..0532790f0a 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -296,8 +296,13 @@ namespace osu.Game.Beatmaps.Formats
switch (type)
{
case EventType.Background:
- string filename = split[2].Trim('"');
- beatmap.BeatmapInfo.Metadata.BackgroundFile = FileSafety.PathStandardise(filename);
+ string bgFilename = split[2].Trim('"');
+ beatmap.BeatmapInfo.Metadata.BackgroundFile = FileSafety.PathStandardise(bgFilename);
+ break;
+
+ case EventType.Video:
+ string videoFilename = split[2].Trim('"');
+ beatmap.BeatmapInfo.Metadata.VideoFile = FileSafety.PathStandardise(videoFilename);
break;
case EventType.Break:
diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs
index 44071d9cc1..a087a52ada 100644
--- a/osu.Game/Beatmaps/IWorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs
@@ -4,6 +4,7 @@
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;
@@ -25,6 +26,11 @@ namespace osu.Game.Beatmaps
///
Texture Background { get; }
+ ///
+ /// Retrieves the video background file for this .
+ ///
+ VideoSprite Video { get; }
+
///
/// Retrieves the audio track for this .
///
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index d8ab411beb..3fc33e9f52 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -19,6 +19,7 @@ using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.UI;
using osu.Game.Skinning;
+using osu.Framework.Graphics.Video;
namespace osu.Game.Beatmaps
{
@@ -186,6 +187,10 @@ 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();
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index 185e5d6df8..e26021d930 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -69,6 +69,7 @@ namespace osu.Game.Configuration
Set(OsuSetting.ShowFpsDisplay, false);
Set(OsuSetting.ShowStoryboard, true);
+ Set(OsuSetting.ShowVideoBackground, true);
Set(OsuSetting.BeatmapSkins, true);
Set(OsuSetting.BeatmapHitsounds, true);
@@ -136,6 +137,7 @@ namespace osu.Game.Configuration
DimLevel,
BlurLevel,
ShowStoryboard,
+ ShowVideoBackground,
KeyOverlay,
ScoreMeter,
FloatingComments,
diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
index a4121acfca..b117d71006 100644
--- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
@@ -21,8 +21,6 @@ namespace osu.Game.Graphics.Containers
private SampleChannel samplePopIn;
private SampleChannel samplePopOut;
- protected virtual bool PlaySamplesOnStateChange => true;
-
protected override bool BlockNonPositionalInput => true;
///
@@ -32,7 +30,7 @@ namespace osu.Game.Graphics.Containers
protected virtual bool DimMainContent => true;
[Resolved(CanBeNull = true)]
- private OsuGame osuGame { get; set; }
+ private OsuGame game { get; set; }
[Resolved]
private PreviewTrackManager previewTrackManager { get; set; }
@@ -42,13 +40,22 @@ namespace osu.Game.Graphics.Containers
[BackgroundDependencyLoader(true)]
private void load(AudioManager audio)
{
- if (osuGame != null)
- OverlayActivationMode.BindTo(osuGame.OverlayActivationMode);
-
samplePopIn = audio.Samples.Get(@"UI/overlay-pop-in");
samplePopOut = audio.Samples.Get(@"UI/overlay-pop-out");
+ }
- State.ValueChanged += onStateChanged;
+ protected override void LoadComplete()
+ {
+ if (game != null)
+ OverlayActivationMode.BindTo(game.OverlayActivationMode);
+
+ OverlayActivationMode.BindValueChanged(mode =>
+ {
+ if (mode.NewValue == OverlayActivation.Disabled)
+ State.Value = Visibility.Hidden;
+ }, true);
+
+ base.LoadComplete();
}
///
@@ -106,26 +113,28 @@ namespace osu.Game.Graphics.Containers
public bool OnReleased(GlobalAction action) => false;
- private void onStateChanged(ValueChangedEvent state)
+ protected override void UpdateState(ValueChangedEvent state)
{
switch (state.NewValue)
{
case Visibility.Visible:
- if (OverlayActivationMode.Value != OverlayActivation.Disabled)
+ if (OverlayActivationMode.Value == OverlayActivation.Disabled)
{
- if (PlaySamplesOnStateChange) samplePopIn?.Play();
- if (BlockScreenWideMouse && DimMainContent) osuGame?.AddBlockingOverlay(this);
+ State.Value = Visibility.Hidden;
+ return;
}
- else
- Hide();
+ samplePopIn?.Play();
+ if (BlockScreenWideMouse && DimMainContent) game?.AddBlockingOverlay(this);
break;
case Visibility.Hidden:
- if (PlaySamplesOnStateChange) samplePopOut?.Play();
- if (BlockScreenWideMouse) osuGame?.RemoveBlockingOverlay(this);
+ samplePopOut?.Play();
+ if (BlockScreenWideMouse) game?.RemoveBlockingOverlay(this);
break;
}
+
+ base.UpdateState(state);
}
protected override void PopOut()
@@ -137,7 +146,7 @@ namespace osu.Game.Graphics.Containers
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
- osuGame?.RemoveBlockingOverlay(this);
+ game?.RemoveBlockingOverlay(this);
}
}
}
diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs
index 2b7635cc88..7683bbcd63 100644
--- a/osu.Game/Graphics/Containers/UserDimContainer.cs
+++ b/osu.Game/Graphics/Containers/UserDimContainer.cs
@@ -35,6 +35,8 @@ namespace osu.Game.Graphics.Containers
protected Bindable ShowStoryboard { get; private set; }
+ protected Bindable ShowVideo { get; private set; }
+
protected double DimLevel => EnableUserDim.Value ? UserDimLevel.Value : 0;
protected override Container Content => dimContent;
@@ -54,10 +56,12 @@ namespace osu.Game.Graphics.Containers
{
UserDimLevel = config.GetBindable(OsuSetting.DimLevel);
ShowStoryboard = config.GetBindable(OsuSetting.ShowStoryboard);
+ ShowVideo = config.GetBindable(OsuSetting.ShowVideoBackground);
EnableUserDim.ValueChanged += _ => UpdateVisuals();
UserDimLevel.ValueChanged += _ => UpdateVisuals();
ShowStoryboard.ValueChanged += _ => UpdateVisuals();
+ ShowVideo.ValueChanged += _ => UpdateVisuals();
StoryboardReplacesBackground.ValueChanged += _ => UpdateVisuals();
}
diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs
index f532302de2..02d928ec66 100644
--- a/osu.Game/Graphics/ScreenshotManager.cs
+++ b/osu.Game/Graphics/ScreenshotManager.cs
@@ -83,11 +83,19 @@ namespace osu.Game.Graphics
const int frames_to_wait = 3;
int framesWaited = 0;
- ScheduledDelegate waitDelegate = host.DrawThread.Scheduler.AddDelayed(() => framesWaited++, 0, true);
- while (framesWaited < frames_to_wait)
- Thread.Sleep(10);
- waitDelegate.Cancel();
+ using (var framesWaitedEvent = new ManualResetEventSlim(false))
+ {
+ ScheduledDelegate waitDelegate = host.DrawThread.Scheduler.AddDelayed(() =>
+ {
+ if (framesWaited++ < frames_to_wait)
+ // ReSharper disable once AccessToDisposedClosure
+ framesWaitedEvent.Set();
+ }, 10, true);
+
+ framesWaitedEvent.Wait();
+ waitDelegate.Cancel();
+ }
}
using (var image = await host.TakeScreenshotAsync())
diff --git a/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs
new file mode 100644
index 0000000000..baca57ea89
--- /dev/null
+++ b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs
@@ -0,0 +1,84 @@
+// 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.UserInterface;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osuTK;
+using osu.Framework.Graphics.Shapes;
+using osuTK.Graphics;
+using osu.Framework.Graphics.Colour;
+
+namespace osu.Game.Graphics.UserInterface
+{
+ public abstract class GradientLineTabControl : PageTabControl
+ {
+ protected Color4 LineColour
+ {
+ get => line.Colour;
+ set => line.Colour = value;
+ }
+
+ private readonly GradientLine line;
+
+ protected GradientLineTabControl()
+ {
+ RelativeSizeAxes = Axes.X;
+
+ AddInternal(line = new GradientLine
+ {
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ });
+ }
+
+ protected override Dropdown CreateDropdown() => null;
+
+ protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
+ {
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ AutoSizeAxes = Axes.X,
+ RelativeSizeAxes = Axes.Y,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(20, 0),
+ };
+
+ private class GradientLine : GridContainer
+ {
+ public GradientLine()
+ {
+ RelativeSizeAxes = Axes.X;
+ Size = new Vector2(0.8f, 1.5f);
+
+ ColumnDimensions = new[]
+ {
+ new Dimension(),
+ new Dimension(mode: GridSizeMode.Relative, size: 0.4f),
+ new Dimension(),
+ };
+
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = ColourInfo.GradientHorizontal(Color4.Transparent, Color4.White)
+ },
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = ColourInfo.GradientHorizontal(Color4.White, Color4.Transparent)
+ },
+ }
+ };
+ }
+ }
+ }
+}
diff --git a/osu.Game/Graphics/UserInterface/PageTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs
index a0d3745180..ddcb626701 100644
--- a/osu.Game/Graphics/UserInterface/PageTabControl.cs
+++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs
@@ -63,7 +63,7 @@ namespace osu.Game.Graphics.UserInterface
Margin = new MarginPadding { Top = 8, Bottom = 8 },
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
- Text = (value as Enum)?.GetDescription() ?? value.ToString(),
+ Text = CreateText(),
Font = OsuFont.GetFont(size: 14)
},
box = new Box
@@ -81,6 +81,8 @@ namespace osu.Game.Graphics.UserInterface
Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Exo, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true);
}
+ protected virtual string CreateText() => (Value as Enum)?.GetDescription() ?? Value.ToString();
+
protected override bool OnHover(HoverEvent e)
{
if (!Active.Value)
diff --git a/osu.Game/Migrations/20190913104727_AddBeatmapVideo.Designer.cs b/osu.Game/Migrations/20190913104727_AddBeatmapVideo.Designer.cs
new file mode 100644
index 0000000000..826233a2b0
--- /dev/null
+++ b/osu.Game/Migrations/20190913104727_AddBeatmapVideo.Designer.cs
@@ -0,0 +1,506 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using osu.Game.Database;
+
+namespace osu.Game.Migrations
+{
+ [DbContext(typeof(OsuDbContext))]
+ [Migration("20190913104727_AddBeatmapVideo")]
+ partial class AddBeatmapVideo
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "2.2.6-servicing-10079");
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ApproachRate");
+
+ b.Property("CircleSize");
+
+ b.Property("DrainRate");
+
+ b.Property("OverallDifficulty");
+
+ b.Property("SliderMultiplier");
+
+ b.Property("SliderTickRate");
+
+ b.HasKey("ID");
+
+ b.ToTable("BeatmapDifficulty");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AudioLeadIn");
+
+ b.Property("BPM");
+
+ b.Property("BaseDifficultyID");
+
+ b.Property("BeatDivisor");
+
+ b.Property("BeatmapSetInfoID");
+
+ b.Property("Countdown");
+
+ b.Property("DistanceSpacing");
+
+ b.Property("GridSize");
+
+ b.Property("Hash");
+
+ b.Property("Hidden");
+
+ b.Property("Length");
+
+ b.Property("LetterboxInBreaks");
+
+ b.Property("MD5Hash");
+
+ b.Property("MetadataID");
+
+ b.Property("OnlineBeatmapID");
+
+ b.Property("Path");
+
+ b.Property("RulesetID");
+
+ b.Property("SpecialStyle");
+
+ b.Property("StackLeniency");
+
+ b.Property("StarDifficulty");
+
+ b.Property("Status");
+
+ b.Property("StoredBookmarks");
+
+ b.Property("TimelineZoom");
+
+ b.Property("Version");
+
+ b.Property("WidescreenStoryboard");
+
+ b.HasKey("ID");
+
+ b.HasIndex("BaseDifficultyID");
+
+ b.HasIndex("BeatmapSetInfoID");
+
+ b.HasIndex("Hash");
+
+ b.HasIndex("MD5Hash");
+
+ b.HasIndex("MetadataID");
+
+ b.HasIndex("OnlineBeatmapID")
+ .IsUnique();
+
+ b.HasIndex("RulesetID");
+
+ b.ToTable("BeatmapInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Artist");
+
+ b.Property("ArtistUnicode");
+
+ b.Property("AudioFile");
+
+ b.Property("AuthorString")
+ .HasColumnName("Author");
+
+ b.Property("BackgroundFile");
+
+ b.Property("PreviewTime");
+
+ b.Property("Source");
+
+ b.Property("Tags");
+
+ b.Property("Title");
+
+ b.Property("TitleUnicode");
+
+ b.Property("VideoFile");
+
+ b.HasKey("ID");
+
+ b.ToTable("BeatmapMetadata");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("BeatmapSetInfoID");
+
+ b.Property("FileInfoID");
+
+ b.Property("Filename")
+ .IsRequired();
+
+ b.HasKey("ID");
+
+ b.HasIndex("BeatmapSetInfoID");
+
+ b.HasIndex("FileInfoID");
+
+ b.ToTable("BeatmapSetFileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("DateAdded");
+
+ b.Property("DeletePending");
+
+ b.Property("Hash");
+
+ b.Property("MetadataID");
+
+ b.Property("OnlineBeatmapSetID");
+
+ b.Property("Protected");
+
+ b.Property("Status");
+
+ b.HasKey("ID");
+
+ b.HasIndex("DeletePending");
+
+ b.HasIndex("Hash")
+ .IsUnique();
+
+ b.HasIndex("MetadataID");
+
+ b.HasIndex("OnlineBeatmapSetID")
+ .IsUnique();
+
+ b.ToTable("BeatmapSetInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Key")
+ .HasColumnName("Key");
+
+ b.Property("RulesetID");
+
+ b.Property("SkinInfoID");
+
+ b.Property("StringValue")
+ .HasColumnName("Value");
+
+ b.Property("Variant");
+
+ b.HasKey("ID");
+
+ b.HasIndex("SkinInfoID");
+
+ b.HasIndex("RulesetID", "Variant");
+
+ b.ToTable("Settings");
+ });
+
+ modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Hash");
+
+ b.Property("ReferenceCount");
+
+ b.HasKey("ID");
+
+ b.HasIndex("Hash")
+ .IsUnique();
+
+ b.HasIndex("ReferenceCount");
+
+ b.ToTable("FileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("IntAction")
+ .HasColumnName("Action");
+
+ b.Property("KeysString")
+ .HasColumnName("Keys");
+
+ b.Property("RulesetID");
+
+ b.Property("Variant");
+
+ b.HasKey("ID");
+
+ b.HasIndex("IntAction");
+
+ b.HasIndex("RulesetID", "Variant");
+
+ b.ToTable("KeyBinding");
+ });
+
+ modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Available");
+
+ b.Property("InstantiationInfo");
+
+ b.Property("Name");
+
+ b.Property("ShortName");
+
+ b.HasKey("ID");
+
+ b.HasIndex("Available");
+
+ b.HasIndex("ShortName")
+ .IsUnique();
+
+ b.ToTable("RulesetInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("FileInfoID");
+
+ b.Property("Filename")
+ .IsRequired();
+
+ b.Property("ScoreInfoID");
+
+ b.HasKey("ID");
+
+ b.HasIndex("FileInfoID");
+
+ b.HasIndex("ScoreInfoID");
+
+ b.ToTable("ScoreFileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Accuracy")
+ .HasColumnType("DECIMAL(1,4)");
+
+ b.Property("BeatmapInfoID");
+
+ b.Property("Combo");
+
+ b.Property("Date");
+
+ b.Property("DeletePending");
+
+ b.Property("Hash");
+
+ b.Property("MaxCombo");
+
+ b.Property("ModsJson")
+ .HasColumnName("Mods");
+
+ b.Property("OnlineScoreID");
+
+ b.Property("PP");
+
+ b.Property("Rank");
+
+ b.Property("RulesetID");
+
+ b.Property("StatisticsJson")
+ .HasColumnName("Statistics");
+
+ b.Property("TotalScore");
+
+ b.Property("UserID")
+ .HasColumnName("UserID");
+
+ b.Property("UserString")
+ .HasColumnName("User");
+
+ b.HasKey("ID");
+
+ b.HasIndex("BeatmapInfoID");
+
+ b.HasIndex("OnlineScoreID")
+ .IsUnique();
+
+ b.HasIndex("RulesetID");
+
+ b.ToTable("ScoreInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("FileInfoID");
+
+ b.Property("Filename")
+ .IsRequired();
+
+ b.Property("SkinInfoID");
+
+ b.HasKey("ID");
+
+ b.HasIndex("FileInfoID");
+
+ b.HasIndex("SkinInfoID");
+
+ b.ToTable("SkinFileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Creator");
+
+ b.Property("DeletePending");
+
+ b.Property("Hash");
+
+ b.Property("Name");
+
+ b.HasKey("ID");
+
+ b.HasIndex("DeletePending");
+
+ b.HasIndex("Hash")
+ .IsUnique();
+
+ b.ToTable("SkinInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
+ .WithMany()
+ .HasForeignKey("BaseDifficultyID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
+ .WithMany("Beatmaps")
+ .HasForeignKey("BeatmapSetInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
+ .WithMany("Beatmaps")
+ .HasForeignKey("MetadataID");
+
+ b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
+ .WithMany()
+ .HasForeignKey("RulesetID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
+ .WithMany("Files")
+ .HasForeignKey("BeatmapSetInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
+ .WithMany()
+ .HasForeignKey("FileInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
+ .WithMany("BeatmapSets")
+ .HasForeignKey("MetadataID");
+ });
+
+ modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
+ {
+ b.HasOne("osu.Game.Skinning.SkinInfo")
+ .WithMany("Settings")
+ .HasForeignKey("SkinInfoID");
+ });
+
+ modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
+ {
+ b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
+ .WithMany()
+ .HasForeignKey("FileInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Scoring.ScoreInfo")
+ .WithMany("Files")
+ .HasForeignKey("ScoreInfoID");
+ });
+
+ modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap")
+ .WithMany("Scores")
+ .HasForeignKey("BeatmapInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
+ .WithMany()
+ .HasForeignKey("RulesetID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
+ {
+ b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
+ .WithMany()
+ .HasForeignKey("FileInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Skinning.SkinInfo")
+ .WithMany("Files")
+ .HasForeignKey("SkinInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/osu.Game/Migrations/20190913104727_AddBeatmapVideo.cs b/osu.Game/Migrations/20190913104727_AddBeatmapVideo.cs
new file mode 100644
index 0000000000..9ed0943acd
--- /dev/null
+++ b/osu.Game/Migrations/20190913104727_AddBeatmapVideo.cs
@@ -0,0 +1,22 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace osu.Game.Migrations
+{
+ public partial class AddBeatmapVideo : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "VideoFile",
+ table: "BeatmapMetadata",
+ nullable: true);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "VideoFile",
+ table: "BeatmapMetadata");
+ }
+ }
+}
diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
index 761dca2801..a6d9d1f3cb 100644
--- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
+++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Migrations
{
#pragma warning disable 612, 618
modelBuilder
- .HasAnnotation("ProductVersion", "2.2.4-servicing-10062");
+ .HasAnnotation("ProductVersion", "2.2.6-servicing-10079");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
@@ -139,6 +139,8 @@ namespace osu.Game.Migrations
b.Property("TitleUnicode");
+ b.Property("VideoFile");
+
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs
index 98f15599fc..147556b78b 100644
--- a/osu.Game/Online/Leaderboards/Leaderboard.cs
+++ b/osu.Game/Online/Leaderboards/Leaderboard.cs
@@ -133,6 +133,10 @@ namespace osu.Game.Online.Leaderboards
});
break;
+ case PlaceholderState.NoneSelected:
+ replacePlaceholder(new MessagePlaceholder(@"Please select a beatmap!"));
+ break;
+
case PlaceholderState.Unavailable:
replacePlaceholder(new MessagePlaceholder(@"Leaderboards are not available for this beatmap!"));
break;
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index 008f8208eb..0b84cfc28a 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
@@ -215,6 +215,7 @@ namespace osu.Game.Online.Leaderboards
Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(1),
ChildrenEnumerable = score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) })
},
},
diff --git a/osu.Game/Online/Leaderboards/PlaceholderState.cs b/osu.Game/Online/Leaderboards/PlaceholderState.cs
index 930e1df484..297241fa73 100644
--- a/osu.Game/Online/Leaderboards/PlaceholderState.cs
+++ b/osu.Game/Online/Leaderboards/PlaceholderState.cs
@@ -9,6 +9,7 @@ namespace osu.Game.Online.Leaderboards
Retrieving,
NetworkFailure,
Unavailable,
+ NoneSelected,
NoScores,
NotLoggedIn,
NotSupporter,
diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs
index dcd58db427..e2a725ec46 100644
--- a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs
+++ b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs
@@ -1,60 +1,37 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Framework.Graphics.UserInterface;
using osu.Game.Screens.Select.Leaderboards;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osuTK;
using osu.Game.Graphics.UserInterface;
-using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Framework.Allocation;
using osuTK.Graphics;
-using osu.Framework.Graphics.Colour;
+using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
+using osu.Framework.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
- public class LeaderboardScopeSelector : PageTabControl
+ public class LeaderboardScopeSelector : GradientLineTabControl
{
protected override bool AddEnumEntriesAutomatically => false;
- protected override Dropdown CreateDropdown() => null;
-
protected override TabItem CreateTabItem(BeatmapLeaderboardScope value) => new ScopeSelectorTabItem(value);
public LeaderboardScopeSelector()
{
- RelativeSizeAxes = Axes.X;
-
AddItem(BeatmapLeaderboardScope.Global);
AddItem(BeatmapLeaderboardScope.Country);
AddItem(BeatmapLeaderboardScope.Friend);
-
- AddInternal(new GradientLine
- {
- Anchor = Anchor.BottomCentre,
- Origin = Anchor.BottomCentre,
- });
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AccentColour = colours.Blue;
+ LineColour = Color4.Gray;
}
- protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
- {
- Anchor = Anchor.BottomCentre,
- Origin = Anchor.BottomCentre,
- AutoSizeAxes = Axes.X,
- RelativeSizeAxes = Axes.Y,
- Direction = FillDirection.Horizontal,
- Spacing = new Vector2(20, 0),
- };
-
private class ScopeSelectorTabItem : PageTabItem
{
public ScopeSelectorTabItem(BeatmapLeaderboardScope value)
@@ -77,43 +54,5 @@ namespace osu.Game.Overlays.BeatmapSet
Text.FadeColour(Color4.White);
}
}
-
- private class GradientLine : GridContainer
- {
- public GradientLine()
- {
- RelativeSizeAxes = Axes.X;
- Size = new Vector2(0.8f, 1.5f);
-
- ColumnDimensions = new[]
- {
- new Dimension(),
- new Dimension(mode: GridSizeMode.Relative, size: 0.4f),
- new Dimension(),
- };
-
- Content = new[]
- {
- new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = ColourInfo.GradientHorizontal(Color4.Transparent, Color4.Gray),
- },
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Gray,
- },
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = ColourInfo.GradientHorizontal(Color4.Gray, Color4.Transparent),
- },
- }
- };
- }
- }
}
}
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs
index 347522fb48..58f5f02956 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs
@@ -171,6 +171,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
+ Spacing = new Vector2(1),
ChildrenEnumerable = score.Mods.Select(m => new ModIcon(m)
{
AutoSizeAxes = Axes.Both,
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs
index 6761d0f710..b9664d7c2f 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs
@@ -172,7 +172,8 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
: this(new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Horizontal
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(1),
})
{
}
diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs
index 7755c8a6a6..dfe3669813 100644
--- a/osu.Game/Overlays/ChangelogOverlay.cs
+++ b/osu.Game/Overlays/ChangelogOverlay.cs
@@ -170,7 +170,7 @@ namespace osu.Game.Overlays
var tcs = new TaskCompletionSource();
var req = new GetChangelogRequest();
- req.Success += res =>
+ req.Success += res => Schedule(() =>
{
// remap streams to builds to ensure model equality
res.Builds.ForEach(b => b.UpdateStream = res.Streams.Find(s => s.Id == b.UpdateStream.Id));
@@ -182,7 +182,7 @@ namespace osu.Game.Overlays
header.Streams.Populate(res.Streams);
tcs.SetResult(true);
- };
+ });
req.Failure += _ => initialFetchTask = null;
req.Perform(API);
diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs
index 2576b38ec8..7596231a3d 100644
--- a/osu.Game/Overlays/Chat/ChatLine.cs
+++ b/osu.Game/Overlays/Chat/ChatLine.cs
@@ -31,6 +31,8 @@ namespace osu.Game.Overlays.Chat
protected virtual float MessagePadding => default_message_padding;
+ private const float timestamp_padding = 65;
+
private const float default_horizontal_padding = 15;
protected virtual float HorizontalPadding => default_horizontal_padding;
@@ -87,7 +89,12 @@ namespace osu.Game.Overlays.Chat
{
Shadow = false,
Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length],
- Font = OsuFont.GetFont(size: TextSize, weight: FontWeight.Bold, italics: true)
+ Truncate = true,
+ EllipsisString = "… :",
+ Font = OsuFont.GetFont(size: TextSize, weight: FontWeight.Bold, italics: true),
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ MaxWidth = default_message_padding - timestamp_padding
};
if (hasBackground)
@@ -142,6 +149,7 @@ namespace osu.Game.Overlays.Chat
new MessageSender(message.Sender)
{
AutoSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = timestamp_padding },
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
Child = effectedUsername,
diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs
index 1022edfe81..cff887865a 100644
--- a/osu.Game/Overlays/Dialog/PopupDialog.cs
+++ b/osu.Game/Overlays/Dialog/PopupDialog.cs
@@ -13,20 +13,17 @@ using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers;
-using osu.Game.Input.Bindings;
using osuTK;
using osuTK.Graphics;
using osuTK.Input;
namespace osu.Game.Overlays.Dialog
{
- public class PopupDialog : OsuFocusedOverlayContainer
+ public abstract class PopupDialog : VisibilityContainer
{
public static readonly float ENTER_DURATION = 500;
public static readonly float EXIT_DURATION = 200;
- protected override bool BlockPositionalInput => false;
-
private readonly Vector2 ringSize = new Vector2(100f);
private readonly Vector2 ringMinifiedSize = new Vector2(20f);
private readonly Vector2 buttonsEnterSpacing = new Vector2(0f, 50f);
@@ -90,7 +87,7 @@ namespace osu.Game.Overlays.Dialog
}
}
- public PopupDialog()
+ protected PopupDialog()
{
RelativeSizeAxes = Axes.Both;
@@ -202,18 +199,6 @@ namespace osu.Game.Overlays.Dialog
};
}
- public override bool OnPressed(GlobalAction action)
- {
- switch (action)
- {
- case GlobalAction.Select:
- Buttons.OfType().FirstOrDefault()?.Click();
- return true;
- }
-
- return base.OnPressed(action);
- }
-
protected override bool OnKeyDown(KeyDownEvent e)
{
if (e.Repeat) return false;
@@ -238,8 +223,6 @@ namespace osu.Game.Overlays.Dialog
protected override void PopIn()
{
- base.PopIn();
-
actionInvoked = false;
// Reset various animations but only if the dialog animation fully completed
@@ -263,7 +246,6 @@ namespace osu.Game.Overlays.Dialog
// This is presumed to always be a sane default "cancel" action.
buttonsContainer.Last().Click();
- base.PopOut();
content.FadeOut(EXIT_DURATION, Easing.InSine);
}
diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs
index aaae7bcf5c..6aaeff8554 100644
--- a/osu.Game/Overlays/DialogOverlay.cs
+++ b/osu.Game/Overlays/DialogOverlay.cs
@@ -5,6 +5,8 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays.Dialog;
using osu.Game.Graphics.Containers;
+using osu.Game.Input.Bindings;
+using System.Linq;
namespace osu.Game.Overlays
{
@@ -41,8 +43,6 @@ namespace osu.Game.Overlays
Show();
}
- protected override bool PlaySamplesOnStateChange => false;
-
protected override bool BlockNonPositionalInput => true;
private void onDialogOnStateChanged(VisibilityContainer dialog, Visibility v)
@@ -74,5 +74,17 @@ namespace osu.Game.Overlays
this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine);
}
+
+ public override bool OnPressed(GlobalAction action)
+ {
+ switch (action)
+ {
+ case GlobalAction.Select:
+ currentDialog?.Buttons.OfType().FirstOrDefault()?.Click();
+ return true;
+ }
+
+ return base.OnPressed(action);
+ }
}
}
diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs
index e54ce44ca2..6362d3dfb0 100644
--- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs
+++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs
@@ -12,12 +12,13 @@ using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osu.Game.Beatmaps;
using osu.Framework.Localisation;
+using osu.Framework.Graphics.Containers;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public abstract class DrawableProfileScore : DrawableProfileRow
{
- private readonly ScoreModsContainer modsContainer;
+ private readonly FillFlowContainer modsContainer;
protected readonly ScoreInfo Score;
protected DrawableProfileScore(ScoreInfo score)
@@ -28,12 +29,12 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
Height = 60;
Children = new Drawable[]
{
- modsContainer = new ScoreModsContainer
+ modsContainer = new FillFlowContainer
{
- AutoSizeAxes = Axes.Y,
+ AutoSizeAxes = Axes.Both,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
- Width = 60,
+ Spacing = new Vector2(1),
Margin = new MarginPadding { Right = 160 }
}
};
diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/ScoreModsContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/ScoreModsContainer.cs
deleted file mode 100644
index 1ce04effa8..0000000000
--- a/osu.Game/Overlays/Profile/Sections/Ranks/ScoreModsContainer.cs
+++ /dev/null
@@ -1,21 +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 osu.Framework.Graphics.Containers;
-using osu.Game.Rulesets.UI;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace osu.Game.Overlays.Profile.Sections.Ranks
-{
- public class ScoreModsContainer : FlowContainer
- {
- protected override IEnumerable ComputeLayoutPositions()
- {
- int count = FlowingChildren.Count();
- for (int i = 0; i < count; i++)
- yield return new Vector2(DrawWidth * i * (count == 1 ? 0 : 1f / (count - 1)), 0);
- }
- }
-}
diff --git a/osu.Game/Overlays/Rankings/DismissableFlag.cs b/osu.Game/Overlays/Rankings/DismissableFlag.cs
new file mode 100644
index 0000000000..7a55b0bba6
--- /dev/null
+++ b/osu.Game/Overlays/Rankings/DismissableFlag.cs
@@ -0,0 +1,55 @@
+// 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.Game.Users.Drawables;
+using osuTK.Graphics;
+using osuTK;
+using osu.Framework.Input.Events;
+using System;
+
+namespace osu.Game.Overlays.Rankings
+{
+ public class DismissableFlag : UpdateableFlag
+ {
+ private const int duration = 200;
+
+ public Action Action;
+
+ private readonly SpriteIcon hoverIcon;
+
+ public DismissableFlag()
+ {
+ AddInternal(hoverIcon = new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Depth = -1,
+ Alpha = 0,
+ Size = new Vector2(10),
+ Icon = FontAwesome.Solid.Times,
+ });
+ }
+
+ protected override bool OnHover(HoverEvent e)
+ {
+ hoverIcon.FadeIn(duration, Easing.OutQuint);
+ this.FadeColour(Color4.Gray, duration, Easing.OutQuint);
+ return base.OnHover(e);
+ }
+
+ protected override void OnHoverLost(HoverLostEvent e)
+ {
+ base.OnHoverLost(e);
+ hoverIcon.FadeOut(duration, Easing.OutQuint);
+ this.FadeColour(Color4.White, duration, Easing.OutQuint);
+ }
+
+ protected override bool OnClick(ClickEvent e)
+ {
+ Action?.Invoke();
+ return true;
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Rankings/HeaderTitle.cs b/osu.Game/Overlays/Rankings/HeaderTitle.cs
new file mode 100644
index 0000000000..cba407ecf7
--- /dev/null
+++ b/osu.Game/Overlays/Rankings/HeaderTitle.cs
@@ -0,0 +1,98 @@
+// 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.Bindables;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Users;
+using osu.Framework.Graphics;
+using osuTK;
+using osu.Game.Graphics;
+using osu.Framework.Allocation;
+
+namespace osu.Game.Overlays.Rankings
+{
+ public class HeaderTitle : CompositeDrawable
+ {
+ private const int spacing = 10;
+ private const int flag_margin = 5;
+ private const int text_size = 40;
+
+ public readonly Bindable Scope = new Bindable();
+ public readonly Bindable Country = new Bindable();
+
+ private readonly SpriteText scopeText;
+ private readonly DismissableFlag flag;
+
+ public HeaderTitle()
+ {
+ AutoSizeAxes = Axes.Both;
+ InternalChild = new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(spacing, 0),
+ Children = new Drawable[]
+ {
+ flag = new DismissableFlag
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Margin = new MarginPadding { Bottom = flag_margin },
+ Size = new Vector2(30, 20),
+ },
+ scopeText = new SpriteText
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Light)
+ },
+ new SpriteText
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Light),
+ Text = @"Ranking"
+ }
+ }
+ };
+
+ flag.Action += () => Country.Value = null;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ scopeText.Colour = colours.Lime;
+ }
+
+ protected override void LoadComplete()
+ {
+ Scope.BindValueChanged(onScopeChanged, true);
+ Country.BindValueChanged(onCountryChanged, true);
+ base.LoadComplete();
+ }
+
+ private void onScopeChanged(ValueChangedEvent scope)
+ {
+ scopeText.Text = scope.NewValue.ToString();
+
+ if (scope.NewValue != RankingsScope.Performance)
+ Country.Value = null;
+ }
+
+ private void onCountryChanged(ValueChangedEvent country)
+ {
+ if (country.NewValue == null)
+ {
+ flag.Hide();
+ return;
+ }
+
+ Scope.Value = RankingsScope.Performance;
+
+ flag.Country = country.NewValue;
+ flag.Show();
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Rankings/RankingsHeader.cs b/osu.Game/Overlays/Rankings/RankingsHeader.cs
new file mode 100644
index 0000000000..6aa3e75df9
--- /dev/null
+++ b/osu.Game/Overlays/Rankings/RankingsHeader.cs
@@ -0,0 +1,129 @@
+// 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.Containers;
+using osu.Framework.Graphics;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Game.Rulesets;
+using osu.Game.Users;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using osuTK;
+using osu.Game.Graphics.UserInterface;
+using System.Collections.Generic;
+
+namespace osu.Game.Overlays.Rankings
+{
+ public class RankingsHeader : CompositeDrawable
+ {
+ private const int content_height = 250;
+
+ public IEnumerable Spotlights
+ {
+ get => dropdown.Items;
+ set => dropdown.Items = value;
+ }
+
+ public readonly Bindable Scope = new Bindable();
+ public readonly Bindable Ruleset = new Bindable();
+ public readonly Bindable Country = new Bindable();
+ public readonly Bindable Spotlight = new Bindable();
+
+ private readonly OsuDropdown dropdown;
+
+ public RankingsHeader()
+ {
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+
+ AddInternal(new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ new RankingsRulesetSelector
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Current = Ruleset
+ },
+ new Container
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ RelativeSizeAxes = Axes.X,
+ Height = content_height,
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ Child = new HeaderBackground(),
+ },
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 20),
+ Children = new Drawable[]
+ {
+ new RankingsScopeSelector
+ {
+ Margin = new MarginPadding { Top = 10 },
+ Current = Scope
+ },
+ new HeaderTitle
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Margin = new MarginPadding { Top = 10 },
+ Scope = { BindTarget = Scope },
+ Country = { BindTarget = Country },
+ },
+ dropdown = new OsuDropdown
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ RelativeSizeAxes = Axes.X,
+ Width = 0.8f,
+ Current = Spotlight,
+ }
+ }
+ },
+ }
+ }
+ }
+ });
+ }
+
+ protected override void LoadComplete()
+ {
+ Scope.BindValueChanged(onScopeChanged, true);
+ base.LoadComplete();
+ }
+
+ private void onScopeChanged(ValueChangedEvent scope) =>
+ dropdown.FadeTo(scope.NewValue == RankingsScope.Spotlights ? 1 : 0, 200, Easing.OutQuint);
+
+ private class HeaderBackground : Sprite
+ {
+ public HeaderBackground()
+ {
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+ RelativeSizeAxes = Axes.Both;
+ FillMode = FillMode.Fill;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ Texture = textures.Get(@"Headers/rankings");
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs b/osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs
new file mode 100644
index 0000000000..3d25e3995a
--- /dev/null
+++ b/osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs
@@ -0,0 +1,56 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.UserInterface;
+using osu.Game.Graphics;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Rulesets;
+using osuTK;
+using System.Linq;
+
+namespace osu.Game.Overlays.Rankings
+{
+ public class RankingsRulesetSelector : PageTabControl
+ {
+ protected override TabItem CreateTabItem(RulesetInfo value) => new RankingsTabItem(value);
+
+ protected override Dropdown CreateDropdown() => null;
+
+ public RankingsRulesetSelector()
+ {
+ AutoSizeAxes = Axes.X;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours, RulesetStore rulesets)
+ {
+ foreach (var r in rulesets.AvailableRulesets)
+ AddItem(r);
+
+ AccentColour = colours.Lime;
+
+ SelectTab(TabContainer.FirstOrDefault());
+ }
+
+ protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
+ {
+ AutoSizeAxes = Axes.X,
+ RelativeSizeAxes = Axes.Y,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(20, 0),
+ };
+
+ private class RankingsTabItem : PageTabItem
+ {
+ public RankingsTabItem(RulesetInfo value)
+ : base(value)
+ {
+ }
+
+ protected override string CreateText() => $"{Value.Name}";
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Rankings/RankingsScopeSelector.cs b/osu.Game/Overlays/Rankings/RankingsScopeSelector.cs
new file mode 100644
index 0000000000..2095bcc61c
--- /dev/null
+++ b/osu.Game/Overlays/Rankings/RankingsScopeSelector.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 osu.Game.Graphics.UserInterface;
+using osu.Framework.Allocation;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays.Rankings
+{
+ public class RankingsScopeSelector : GradientLineTabControl
+ {
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ AccentColour = LineColour = Color4.Black;
+ }
+ }
+
+ public enum RankingsScope
+ {
+ Performance,
+ Spotlights,
+ Score,
+ Country
+ }
+}
diff --git a/osu.Game/Overlays/Rankings/Spotlight.cs b/osu.Game/Overlays/Rankings/Spotlight.cs
new file mode 100644
index 0000000000..e956b4f449
--- /dev/null
+++ b/osu.Game/Overlays/Rankings/Spotlight.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.
+
+using Newtonsoft.Json;
+
+namespace osu.Game.Overlays.Rankings
+{
+ public class Spotlight
+ {
+ [JsonProperty("id")]
+ public int Id;
+
+ [JsonProperty("text")]
+ public string Text;
+
+ public override string ToString() => Text;
+ }
+}
diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs
index 01cdc9aa32..9be34c3073 100644
--- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs
@@ -23,8 +23,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
},
new SettingsCheckbox
{
- LabelText = "Rotate cursor when dragging",
- Bindable = config.GetBindable(OsuSetting.CursorRotation)
+ LabelText = "Video",
+ Bindable = config.GetBindable(OsuSetting.ShowVideoBackground)
},
new SettingsEnumDropdown
{
diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs
similarity index 71%
rename from osu.Game/Overlays/Settings/Sections/Graphics/MainMenuSettings.cs
rename to osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs
index 92f64d0e14..dd822fedb6 100644
--- a/osu.Game/Overlays/Settings/Sections/Graphics/MainMenuSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs
@@ -6,7 +6,7 @@ using osu.Game.Configuration;
namespace osu.Game.Overlays.Settings.Sections.Graphics
{
- public class MainMenuSettings : SettingsSubsection
+ public class UserInterfaceSettings : SettingsSubsection
{
protected override string Header => "User Interface";
@@ -15,6 +15,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
{
Children = new[]
{
+ new SettingsCheckbox
+ {
+ LabelText = "Rotate cursor when dragging",
+ Bindable = config.GetBindable(OsuSetting.CursorRotation)
+ },
new SettingsCheckbox
{
LabelText = "Parallax",
diff --git a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs
index 3d6086d3ea..89caa3dc8f 100644
--- a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Settings.Sections
new RendererSettings(),
new LayoutSettings(),
new DetailSettings(),
- new MainMenuSettings(),
+ new UserInterfaceSettings(),
};
}
}
diff --git a/osu.Game/Rulesets/Objects/BarLine.cs b/osu.Game/Rulesets/Objects/BarLine.cs
new file mode 100644
index 0000000000..a5c716e127
--- /dev/null
+++ b/osu.Game/Rulesets/Objects/BarLine.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.
+
+namespace osu.Game.Rulesets.Objects
+{
+ ///
+ /// A hit object representing the end of a bar.
+ ///
+ public class BarLine : HitObject
+ {
+ ///
+ /// Whether this barline is a prominent beat (based on time signature of beatmap).
+ ///
+ public bool Major;
+ }
+}
diff --git a/osu.Game/Rulesets/Objects/BarLineGenerator.cs b/osu.Game/Rulesets/Objects/BarLineGenerator.cs
new file mode 100644
index 0000000000..ce571d7b17
--- /dev/null
+++ b/osu.Game/Rulesets/Objects/BarLineGenerator.cs
@@ -0,0 +1,58 @@
+// 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.Framework.MathUtils;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Objects.Types;
+
+namespace osu.Game.Rulesets.Objects
+{
+ public class BarLineGenerator
+ {
+ ///
+ /// The generated bar lines.
+ ///
+ public readonly List BarLines = new List();
+
+ ///
+ /// Constructs and generates bar lines for provided beatmap.
+ ///
+ /// The beatmap to generate bar lines for.
+ public BarLineGenerator(IBeatmap beatmap)
+ {
+ if (beatmap.HitObjects.Count == 0)
+ return;
+
+ HitObject lastObject = beatmap.HitObjects.Last();
+ double lastHitTime = 1 + ((lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime);
+
+ var timingPoints = beatmap.ControlPointInfo.TimingPoints;
+
+ if (timingPoints.Count == 0)
+ return;
+
+ for (int i = 0; i < timingPoints.Count; i++)
+ {
+ TimingControlPoint currentTimingPoint = timingPoints[i];
+ int currentBeat = 0;
+
+ // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object
+ double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - currentTimingPoint.BeatLength : lastHitTime + currentTimingPoint.BeatLength * (int)currentTimingPoint.TimeSignature;
+
+ double barLength = currentTimingPoint.BeatLength * (int)currentTimingPoint.TimeSignature;
+
+ for (double t = currentTimingPoint.Time; Precision.DefinitelyBigger(endTime, t); t += barLength, currentBeat++)
+ {
+ BarLines.Add(new BarLine
+ {
+ StartTime = t,
+ Major = currentBeat % (int)currentTimingPoint.TimeSignature == 0
+ });
+ }
+ }
+ }
+ }
+}
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index db87d4b4f2..00b57f7249 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -153,6 +153,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
if (UseTransformStateManagement)
{
+ LifetimeEnd = double.MaxValue;
+
double transformTime = HitObject.StartTime - InitialLifetimeOffset;
base.ApplyTransformsAt(transformTime, true);
@@ -170,6 +172,9 @@ namespace osu.Game.Rulesets.Objects.Drawables
state.Value = newState;
}
}
+
+ if (state.Value != ArmedState.Idle && LifetimeEnd == double.MaxValue)
+ Expire();
}
else
state.Value = newState;
@@ -200,6 +205,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
///
/// Apply transforms based on the current . Previous states are automatically cleared.
+ /// In the case of a non-idle , and if was not set during this call, will be invoked.
///
/// The new armed state.
protected virtual void UpdateStateTransforms(ArmedState state)
@@ -308,7 +314,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
///
/// This is only used as an optimisation to delay the initial update of this and may be tuned more aggressively if required.
/// It is indirectly used to decide the automatic transform offset provided to .
- /// A more accurate should be set inside for an state.
+ /// A more accurate should be set for further optimisation (in , for example).
///
protected virtual double InitialLifetimeOffset => 10000;
diff --git a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs
index 7ecdc0715b..c2947c0aca 100644
--- a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs
+++ b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs
@@ -14,8 +14,9 @@ namespace osu.Game.Rulesets.Replays.Types
///
/// Populates this using values from a .
///
- /// The to extract values from.
+ /// The to extract values from.
/// The beatmap.
- void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap);
+ /// The last post-conversion , used to fill in missing delta information. May be null.
+ void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null);
}
}
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index e4f20c27b4..18c2a2ca01 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Scoring
///
/// The current health.
///
- public readonly BindableDouble Health = new BindableDouble { MinValue = 0, MaxValue = 1 };
+ public readonly BindableDouble Health = new BindableDouble(1) { MinValue = 0, MaxValue = 1 };
///
/// The current combo.
@@ -233,6 +233,8 @@ namespace osu.Game.Rulesets.Scoring
drawableRuleset.OnRevertResult += revertResult;
ApplyBeatmap(drawableRuleset.Beatmap);
+
+ Reset(false);
SimulateAutoplay(drawableRuleset.Beatmap);
Reset(true);
diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs
index e25c3bd0e7..98e27240d3 100644
--- a/osu.Game/Rulesets/UI/RulesetInputManager.cs
+++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs
@@ -137,9 +137,9 @@ namespace osu.Game.Rulesets.UI
{
}
- public bool OnPressed(T action) => Target.Children.OfType>().Any(c => c.OnPressed(action));
+ public bool OnPressed(T action) => Target.Children.OfType>().Any(c => c.OnPressed(action, Clock.ElapsedFrameTime > 0));
- public bool OnReleased(T action) => Target.Children.OfType>().Any(c => c.OnReleased(action));
+ public bool OnReleased(T action) => Target.Children.OfType>().Any(c => c.OnReleased(action, Clock.ElapsedFrameTime > 0));
}
#endregion
diff --git a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs
index 2e4b4b3a9a..2cdd0c4b5e 100644
--- a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs
+++ b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs
@@ -218,6 +218,7 @@ namespace osu.Game.Scoring.Legacy
private void readLegacyReplay(Replay replay, StreamReader reader)
{
float lastTime = 0;
+ ReplayFrame currentFrame = null;
foreach (var l in reader.ReadToEnd().Split(','))
{
@@ -240,23 +241,25 @@ namespace osu.Game.Scoring.Legacy
if (diff < 0)
continue;
- replay.Frames.Add(convertFrame(new LegacyReplayFrame(lastTime,
+ currentFrame = convertFrame(new LegacyReplayFrame(lastTime,
Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE),
Parsing.ParseFloat(split[2], Parsing.MAX_COORDINATE_VALUE),
- (ReplayButtonState)Parsing.ParseInt(split[3]))));
+ (ReplayButtonState)Parsing.ParseInt(split[3])), currentFrame);
+
+ replay.Frames.Add(currentFrame);
}
}
- private ReplayFrame convertFrame(LegacyReplayFrame legacyFrame)
+ private ReplayFrame convertFrame(LegacyReplayFrame currentFrame, ReplayFrame lastFrame)
{
var convertible = currentRuleset.CreateConvertibleReplayFrame();
if (convertible == null)
throw new InvalidOperationException($"Legacy replay cannot be converted for the ruleset: {currentRuleset.Description}");
- convertible.ConvertFrom(legacyFrame, currentBeatmap);
+ convertible.ConvertFrom(currentFrame, currentBeatmap, lastFrame);
var frame = (ReplayFrame)convertible;
- frame.Time = legacyFrame.Time;
+ frame.Time = currentFrame.Time;
return frame;
}
diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs
index 5225740d0b..2730b0b90d 100644
--- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs
+++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs
@@ -178,7 +178,7 @@ namespace osu.Game.Screens.Backgrounds
BlurAmount.ValueChanged += _ => UpdateVisuals();
}
- protected override bool ShowDimContent => !ShowStoryboard.Value || !StoryboardReplacesBackground.Value; // The background needs to be hidden in the case of it being replaced by the storyboard
+ protected override bool ShowDimContent => !ShowStoryboard.Value || !StoryboardReplacesBackground.Value || !ShowVideo.Value; // The background needs to be hidden in the case of it being replaced by the storyboard
protected override void UpdateVisuals()
{
diff --git a/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs b/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs
index 299059407c..4b8720fe1c 100644
--- a/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs
@@ -4,6 +4,7 @@
using System.Collections.Generic;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
+using osu.Framework.Graphics.Video;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
@@ -33,6 +34,8 @@ namespace osu.Game.Screens.Edit
public Texture Background => workingBeatmap.Background;
+ public VideoSprite Video => workingBeatmap.Video;
+
public Track Track => workingBeatmap.Track;
public Waveform Waveform => workingBeatmap.Waveform;
diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs
index 073ab639e3..17f999d519 100644
--- a/osu.Game/Screens/Menu/Disclaimer.cs
+++ b/osu.Game/Screens/Menu/Disclaimer.cs
@@ -173,7 +173,11 @@ namespace osu.Game.Screens.Menu
.Then(5500)
.FadeOut(250)
.ScaleTo(0.9f, 250, Easing.InQuint)
- .Finally(d => this.Push(nextScreen));
+ .Finally(d =>
+ {
+ if (nextScreen != null)
+ this.Push(nextScreen);
+ });
}
}
}
diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs
index 499b5089f6..a006877082 100644
--- a/osu.Game/Screens/Menu/MainMenu.cs
+++ b/osu.Game/Screens/Menu/MainMenu.cs
@@ -11,6 +11,7 @@ using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
+using osu.Game.Online.API;
using osu.Game.Overlays;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Charts;
@@ -44,6 +45,12 @@ namespace osu.Game.Screens.Menu
[Resolved(canBeNull: true)]
private MusicController music { get; set; }
+ [Resolved(canBeNull: true)]
+ private LoginOverlay login { get; set; }
+
+ [Resolved]
+ private IAPIProvider api { get; set; }
+
private BackgroundScreenDefault background;
protected override BackgroundScreen CreateBackground() => background;
@@ -133,6 +140,8 @@ namespace osu.Game.Screens.Menu
Beatmap.ValueChanged += beatmap_ValueChanged;
}
+ private bool loginDisplayed;
+
protected override void LogoArriving(OsuLogo logo, bool resuming)
{
base.LogoArriving(logo, resuming);
@@ -151,6 +160,21 @@ namespace osu.Game.Screens.Menu
sideFlashes.Delay(FADE_IN_DURATION).FadeIn(64, Easing.InQuint);
}
+ else if (!api.IsLoggedIn)
+ {
+ logo.Action += displayLogin;
+ }
+
+ bool displayLogin()
+ {
+ if (!loginDisplayed)
+ {
+ Scheduler.AddDelayed(() => login?.Show(), 500);
+ loginDisplayed = true;
+ }
+
+ return true;
+ }
}
protected override void LogoSuspending(OsuLogo logo)
diff --git a/osu.Game/Screens/Play/DimmableVideo.cs b/osu.Game/Screens/Play/DimmableVideo.cs
new file mode 100644
index 0000000000..4d6c10d69d
--- /dev/null
+++ b/osu.Game/Screens/Play/DimmableVideo.cs
@@ -0,0 +1,88 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Video;
+using osu.Game.Graphics.Containers;
+using osuTK.Graphics;
+
+namespace osu.Game.Screens.Play
+{
+ public class DimmableVideo : UserDimContainer
+ {
+ private readonly VideoSprite video;
+ private DrawableVideo drawableVideo;
+
+ public DimmableVideo(VideoSprite video)
+ {
+ this.video = video;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ initializeVideo(false);
+ }
+
+ protected override void LoadComplete()
+ {
+ ShowVideo.BindValueChanged(_ => initializeVideo(true), true);
+ base.LoadComplete();
+ }
+
+ protected override bool ShowDimContent => ShowVideo.Value && DimLevel < 1;
+
+ private void initializeVideo(bool async)
+ {
+ if (video == null)
+ return;
+
+ if (drawableVideo != null)
+ return;
+
+ if (!ShowVideo.Value)
+ return;
+
+ drawableVideo = new DrawableVideo(video);
+
+ if (async)
+ LoadComponentAsync(drawableVideo, Add);
+ else
+ Add(drawableVideo);
+ }
+
+ private class DrawableVideo : Container
+ {
+ public DrawableVideo(VideoSprite video)
+ {
+ RelativeSizeAxes = Axes.Both;
+ Masking = true;
+
+ video.RelativeSizeAxes = Axes.Both;
+ video.FillMode = FillMode.Fit;
+ video.Anchor = Anchor.Centre;
+ video.Origin = Anchor.Centre;
+
+ AddRangeInternal(new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ },
+ video,
+ });
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(GameplayClock clock)
+ {
+ if (clock != null)
+ Clock = clock;
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Play/GameplayClock.cs b/osu.Game/Screens/Play/GameplayClock.cs
index b1948d02d5..379c4c89ed 100644
--- a/osu.Game/Screens/Play/GameplayClock.cs
+++ b/osu.Game/Screens/Play/GameplayClock.cs
@@ -42,5 +42,7 @@ namespace osu.Game.Screens.Play
public double FramesPerSecond => underlyingClock.FramesPerSecond;
public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo;
+
+ public IClock Source => underlyingClock;
}
}
diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs
index eee7235a6e..0f9edf5606 100644
--- a/osu.Game/Screens/Play/HUDOverlay.cs
+++ b/osu.Game/Screens/Play/HUDOverlay.cs
@@ -231,7 +231,6 @@ namespace osu.Game.Screens.Play
protected virtual KeyCounterDisplay CreateKeyCounter() => new KeyCounterDisplay
{
- FadeTime = 50,
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Margin = new MarginPadding(10),
diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs
index 88a62ac8d4..f4109a63d0 100644
--- a/osu.Game/Screens/Play/KeyCounter.cs
+++ b/osu.Game/Screens/Play/KeyCounter.cs
@@ -1,8 +1,6 @@
// 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.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -22,9 +20,6 @@ namespace osu.Game.Screens.Play
private Container textLayer;
private SpriteText countSpriteText;
- private readonly List states = new List();
- private KeyCounterState currentState;
-
public bool IsCounting { get; set; } = true;
private int countPresses;
@@ -52,20 +47,30 @@ namespace osu.Game.Screens.Play
{
isLit = value;
updateGlowSprite(value);
-
- if (value && IsCounting)
- {
- CountPresses++;
- saveState();
- }
}
}
}
+ public void Increment()
+ {
+ if (!IsCounting)
+ return;
+
+ CountPresses++;
+ }
+
+ public void Decrement()
+ {
+ if (!IsCounting)
+ return;
+
+ CountPresses--;
+ }
+
//further: change default values here and in KeyCounterCollection if needed, instead of passing them in every constructor
public Color4 KeyDownTextColor { get; set; } = Color4.DarkGray;
public Color4 KeyUpTextColor { get; set; } = Color4.White;
- public int FadeTime { get; set; }
+ public double FadeTime { get; set; }
protected KeyCounter(string name)
{
@@ -73,11 +78,8 @@ namespace osu.Game.Screens.Play
}
[BackgroundDependencyLoader(true)]
- private void load(TextureStore textures, GameplayClock clock)
+ private void load(TextureStore textures)
{
- if (clock != null)
- Clock = clock;
-
Children = new Drawable[]
{
buttonSprite = new Sprite
@@ -132,42 +134,16 @@ namespace osu.Game.Screens.Play
{
if (show)
{
- glowSprite.FadeIn(FadeTime);
- textLayer.FadeColour(KeyDownTextColor, FadeTime);
+ double remainingFadeTime = FadeTime * (1 - glowSprite.Alpha);
+ glowSprite.FadeIn(remainingFadeTime, Easing.OutQuint);
+ textLayer.FadeColour(KeyDownTextColor, remainingFadeTime, Easing.OutQuint);
}
else
{
- glowSprite.FadeOut(FadeTime);
- textLayer.FadeColour(KeyUpTextColor, FadeTime);
+ double remainingFadeTime = 8 * FadeTime * glowSprite.Alpha;
+ glowSprite.FadeOut(remainingFadeTime, Easing.OutQuint);
+ textLayer.FadeColour(KeyUpTextColor, remainingFadeTime, Easing.OutQuint);
}
}
-
- public void ResetCount()
- {
- CountPresses = 0;
- states.Clear();
- }
-
- protected override void Update()
- {
- base.Update();
-
- if (currentState?.Time > Clock.CurrentTime)
- restoreStateTo(Clock.CurrentTime);
- }
-
- private void saveState()
- {
- if (currentState == null || currentState.Time < Clock.CurrentTime)
- states.Add(currentState = new KeyCounterState(Clock.CurrentTime, CountPresses));
- }
-
- private void restoreStateTo(double time)
- {
- states.RemoveAll(state => state.Time > time);
-
- currentState = states.LastOrDefault();
- CountPresses = currentState?.Count ?? 0;
- }
}
}
diff --git a/osu.Game/Screens/Play/KeyCounterAction.cs b/osu.Game/Screens/Play/KeyCounterAction.cs
index 8deac653ad..f60ad7aa5a 100644
--- a/osu.Game/Screens/Play/KeyCounterAction.cs
+++ b/osu.Game/Screens/Play/KeyCounterAction.cs
@@ -1,11 +1,9 @@
// 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.Input.Bindings;
-
namespace osu.Game.Screens.Play
{
- public class KeyCounterAction : KeyCounter, IKeyBindingHandler
+ public class KeyCounterAction : KeyCounter
where T : struct
{
public T Action { get; }
@@ -16,15 +14,25 @@ namespace osu.Game.Screens.Play
Action = action;
}
- public bool OnPressed(T action)
+ public bool OnPressed(T action, bool forwards)
{
- if (action.Equals(Action)) IsLit = true;
+ if (!action.Equals(Action))
+ return false;
+
+ IsLit = true;
+ if (forwards)
+ Increment();
return false;
}
- public bool OnReleased(T action)
+ public bool OnReleased(T action, bool forwards)
{
- if (action.Equals(Action)) IsLit = false;
+ if (!action.Equals(Action))
+ return false;
+
+ IsLit = false;
+ if (!forwards)
+ Decrement();
return false;
}
}
diff --git a/osu.Game/Screens/Play/KeyCounterDisplay.cs b/osu.Game/Screens/Play/KeyCounterDisplay.cs
index d5967f5899..1edb95ca46 100644
--- a/osu.Game/Screens/Play/KeyCounterDisplay.cs
+++ b/osu.Game/Screens/Play/KeyCounterDisplay.cs
@@ -17,6 +17,7 @@ namespace osu.Game.Screens.Play
public class KeyCounterDisplay : FillFlowContainer
{
private const int duration = 100;
+ private const double key_fade_time = 80;
public readonly Bindable Visible = new Bindable(true);
private readonly Bindable configVisibility = new Bindable();
@@ -33,17 +34,11 @@ namespace osu.Game.Screens.Play
base.Add(key);
key.IsCounting = IsCounting;
- key.FadeTime = FadeTime;
+ key.FadeTime = key_fade_time;
key.KeyDownTextColor = KeyDownTextColor;
key.KeyUpTextColor = KeyUpTextColor;
}
- public void ResetCount()
- {
- foreach (var counter in Children)
- counter.ResetCount();
- }
-
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
@@ -68,22 +63,6 @@ namespace osu.Game.Screens.Play
}
}
- private int fadeTime;
-
- public int FadeTime
- {
- get => fadeTime;
- set
- {
- if (value != fadeTime)
- {
- fadeTime = value;
- foreach (var child in Children)
- child.FadeTime = value;
- }
- }
- }
-
private Color4 keyDownTextColor = Color4.DarkGray;
public Color4 KeyDownTextColor
@@ -123,11 +102,6 @@ namespace osu.Game.Screens.Play
private Receptor receptor;
- public Receptor GetReceptor()
- {
- return receptor ?? (receptor = new Receptor(this));
- }
-
public void SetReceptor(Receptor receptor)
{
if (this.receptor != null)
diff --git a/osu.Game/Screens/Play/KeyCounterKeyboard.cs b/osu.Game/Screens/Play/KeyCounterKeyboard.cs
index d9b6dca79d..29188b6b59 100644
--- a/osu.Game/Screens/Play/KeyCounterKeyboard.cs
+++ b/osu.Game/Screens/Play/KeyCounterKeyboard.cs
@@ -18,7 +18,12 @@ namespace osu.Game.Screens.Play
protected override bool OnKeyDown(KeyDownEvent e)
{
- if (e.Key == Key) IsLit = true;
+ if (e.Key == Key)
+ {
+ IsLit = true;
+ Increment();
+ }
+
return base.OnKeyDown(e);
}
diff --git a/osu.Game/Screens/Play/KeyCounterMouse.cs b/osu.Game/Screens/Play/KeyCounterMouse.cs
index 95fa58e5c0..828441de6e 100644
--- a/osu.Game/Screens/Play/KeyCounterMouse.cs
+++ b/osu.Game/Screens/Play/KeyCounterMouse.cs
@@ -36,7 +36,12 @@ namespace osu.Game.Screens.Play
protected override bool OnMouseDown(MouseDownEvent e)
{
- if (e.Button == Button) IsLit = true;
+ if (e.Button == Button)
+ {
+ IsLit = true;
+ Increment();
+ }
+
return base.OnMouseDown(e);
}
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index 3f1603eabe..309f4837e5 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -80,6 +80,7 @@ namespace osu.Game.Screens.Play
protected GameplayClockContainer GameplayClockContainer { get; private set; }
protected DimmableStoryboard DimmableStoryboard { get; private set; }
+ protected DimmableVideo DimmableVideo { get; private set; }
[Cached]
[Cached(Type = typeof(IBindable>))]
@@ -142,6 +143,7 @@ namespace osu.Game.Screens.Play
private void addUnderlayComponents(Container target)
{
+ target.Add(DimmableVideo = new DimmableVideo(Beatmap.Value.Video) { RelativeSizeAxes = Axes.Both });
target.Add(DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both });
}
@@ -502,15 +504,18 @@ namespace osu.Game.Screens.Play
return true;
}
- if (pauseCooldownActive && !GameplayClockContainer.IsPaused.Value)
- // still want to block if we are within the cooldown period and not already paused.
- return true;
-
- if (HasFailed && ValidForResume && !FailOverlay.IsPresent)
- // ValidForResume is false when restarting
+ // ValidForResume is false when restarting
+ if (ValidForResume)
{
- failAnimation.FinishTransforms(true);
- return true;
+ if (pauseCooldownActive && !GameplayClockContainer.IsPaused.Value)
+ // still want to block if we are within the cooldown period and not already paused.
+ return true;
+
+ if (HasFailed && !FailOverlay.IsPresent)
+ {
+ failAnimation.FinishTransforms(true);
+ return true;
+ }
}
GameplayClockContainer.ResetLocalAdjustments();
diff --git a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs
index 1c8628f704..ff64f35a18 100644
--- a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs
+++ b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs
@@ -15,6 +15,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
private readonly PlayerSliderBar dimSliderBar;
private readonly PlayerSliderBar blurSliderBar;
private readonly PlayerCheckbox showStoryboardToggle;
+ private readonly PlayerCheckbox showVideoToggle;
private readonly PlayerCheckbox beatmapSkinsToggle;
private readonly PlayerCheckbox beatmapHitsoundsToggle;
@@ -37,6 +38,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
Text = "Toggles:"
},
showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" },
+ showVideoToggle = new PlayerCheckbox { LabelText = "Video" },
beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" },
beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" }
};
@@ -48,6 +50,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
dimSliderBar.Bindable = config.GetBindable(OsuSetting.DimLevel);
blurSliderBar.Bindable = config.GetBindable(OsuSetting.BlurLevel);
showStoryboardToggle.Current = config.GetBindable(OsuSetting.ShowStoryboard);
+ showVideoToggle.Current = config.GetBindable(OsuSetting.ShowVideoBackground);
beatmapSkinsToggle.Current = config.GetBindable(OsuSetting.BeatmapSkins);
beatmapHitsoundsToggle.Current = config.GetBindable