diff --git a/.gitattributes b/.gitattributes
index baf69b41d1..e3ffd343db 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -14,6 +14,7 @@ App.config text eol=crlf
*.bat text eol=crlf
*.cmd text eol=crlf
*.snippet text eol=crlf
+*.manifest text eol=crlf
# Check out with lf (UNIX) line endings
*.sh text eol=lf
diff --git a/app.manifest b/app.manifest
new file mode 100644
index 0000000000..533c6ff208
--- /dev/null
+++ b/app.manifest
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
diff --git a/osu-framework b/osu-framework
index 02d7a0fa47..16e6a453db 160000
--- a/osu-framework
+++ b/osu-framework
@@ -1 +1 @@
-Subproject commit 02d7a0fa4798d197cd08570ee48951edbb7c7860
+Subproject commit 16e6a453db9a8f4454238a2911eb5f1444b7ec2a
diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
index a18db9477a..6b7509a381 100644
--- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
+++ b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs
index 8930b09089..cfe0fc5cec 100644
--- a/osu.Game.Rulesets.Catch/CatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs
@@ -12,6 +12,7 @@ using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Replays.Types;
+using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Catch
{
@@ -29,6 +30,44 @@ namespace osu.Game.Rulesets.Catch
new KeyBinding(InputKey.Shift, CatchAction.Dash),
};
+ public override IEnumerable ConvertLegacyMods(LegacyMods mods)
+ {
+ if (mods.HasFlag(LegacyMods.Nightcore))
+ yield return new CatchModNightcore();
+ else if (mods.HasFlag(LegacyMods.DoubleTime))
+ yield return new CatchModDoubleTime();
+
+ if (mods.HasFlag(LegacyMods.Autoplay))
+ yield return new CatchModAutoplay();
+
+ if (mods.HasFlag(LegacyMods.Easy))
+ yield return new CatchModEasy();
+
+ if (mods.HasFlag(LegacyMods.Flashlight))
+ yield return new CatchModFlashlight();
+
+ if (mods.HasFlag(LegacyMods.HalfTime))
+ yield return new CatchModHalfTime();
+
+ if (mods.HasFlag(LegacyMods.HardRock))
+ yield return new CatchModHardRock();
+
+ if (mods.HasFlag(LegacyMods.Hidden))
+ yield return new CatchModHidden();
+
+ 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)
{
switch (type)
diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs
index 9e48e8de74..df7578799f 100644
--- a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs
+++ b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs
@@ -1,13 +1,88 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Framework.MathUtils;
+using osu.Game.Rulesets.Catch.Objects;
+using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Mods;
+using System;
namespace osu.Game.Rulesets.Catch.Mods
{
- public class CatchModHardRock : ModHardRock
+ public class CatchModHardRock : ModHardRock, IApplicableToHitObject
{
public override double ScoreMultiplier => 1.12;
public override bool Ranked => true;
+
+ private float lastStartX;
+ private int lastStartTime;
+
+ public void ApplyToHitObject(CatchHitObject hitObject)
+ {
+ float position = hitObject.X;
+ int startTime = (int)hitObject.StartTime;
+
+ if (lastStartX == 0)
+ {
+ lastStartX = position;
+ lastStartTime = startTime;
+ return;
+ }
+
+ float diff = lastStartX - position;
+ int timeDiff = startTime - lastStartTime;
+
+ if (timeDiff > 1000)
+ {
+ lastStartX = position;
+ lastStartTime = startTime;
+ return;
+ }
+
+ if (diff == 0)
+ {
+ bool right = RNG.NextBool();
+
+ float rand = Math.Min(20, (float)RNG.NextDouble(0, timeDiff / 4d)) / CatchPlayfield.BASE_WIDTH;
+
+ if (right)
+ {
+ if (position + rand <= 1)
+ position += rand;
+ else
+ position -= rand;
+ }
+ else
+ {
+ if (position - rand >= 0)
+ position -= rand;
+ else
+ position += rand;
+ }
+
+ hitObject.X = position;
+
+ return;
+ }
+
+ if (Math.Abs(diff) < timeDiff / 3d)
+ {
+ if (diff > 0)
+ {
+ if (position - diff > 0)
+ position -= diff;
+ }
+ else
+ {
+ if (position - diff < 1)
+ position -= diff;
+ }
+ }
+
+ hitObject.X = position;
+
+ lastStartX = position;
+ lastStartTime = startTime;
+ }
}
}
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index 7f37f55d14..0546cbc174 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -14,6 +14,7 @@ using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Replays.Types;
+using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Mania
{
@@ -21,6 +22,74 @@ namespace osu.Game.Rulesets.Mania
{
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaRulesetContainer(this, beatmap, isForCurrentRuleset);
+ public override IEnumerable ConvertLegacyMods(LegacyMods mods)
+ {
+ if (mods.HasFlag(LegacyMods.Nightcore))
+ yield return new ManiaModNightcore();
+ else if (mods.HasFlag(LegacyMods.DoubleTime))
+ yield return new ManiaModDoubleTime();
+
+ if (mods.HasFlag(LegacyMods.Autoplay))
+ yield return new ManiaModAutoplay();
+
+ if (mods.HasFlag(LegacyMods.Easy))
+ yield return new ManiaModEasy();
+
+ if (mods.HasFlag(LegacyMods.FadeIn))
+ yield return new ManiaModFadeIn();
+
+ if (mods.HasFlag(LegacyMods.Flashlight))
+ yield return new ManiaModFlashlight();
+
+ if (mods.HasFlag(LegacyMods.HalfTime))
+ yield return new ManiaModHalfTime();
+
+ if (mods.HasFlag(LegacyMods.HardRock))
+ yield return new ManiaModHardRock();
+
+ if (mods.HasFlag(LegacyMods.Hidden))
+ yield return new ManiaModHidden();
+
+ if (mods.HasFlag(LegacyMods.Key1))
+ yield return new ManiaModKey1();
+
+ if (mods.HasFlag(LegacyMods.Key2))
+ yield return new ManiaModKey2();
+
+ if (mods.HasFlag(LegacyMods.Key3))
+ yield return new ManiaModKey3();
+
+ if (mods.HasFlag(LegacyMods.Key4))
+ yield return new ManiaModKey4();
+
+ if (mods.HasFlag(LegacyMods.Key5))
+ yield return new ManiaModKey5();
+
+ if (mods.HasFlag(LegacyMods.Key6))
+ yield return new ManiaModKey6();
+
+ if (mods.HasFlag(LegacyMods.Key7))
+ yield return new ManiaModKey7();
+
+ if (mods.HasFlag(LegacyMods.Key8))
+ yield return new ManiaModKey8();
+
+ if (mods.HasFlag(LegacyMods.Key9))
+ yield return new ManiaModKey9();
+
+ 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)
{
switch (type)
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index 37e6ec3817..e0ecee97a3 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -21,6 +21,7 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Replays.Types;
+using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Osu
{
@@ -66,6 +67,53 @@ namespace osu.Game.Rulesets.Osu
};
}
+ public override IEnumerable ConvertLegacyMods(LegacyMods mods)
+ {
+ if (mods.HasFlag(LegacyMods.Nightcore))
+ yield return new OsuModNightcore();
+ else if (mods.HasFlag(LegacyMods.DoubleTime))
+ yield return new OsuModDoubleTime();
+
+ if (mods.HasFlag(LegacyMods.Autopilot))
+ yield return new OsuModAutopilot();
+
+ if (mods.HasFlag(LegacyMods.Autoplay))
+ yield return new OsuModAutoplay();
+
+ if (mods.HasFlag(LegacyMods.Easy))
+ yield return new OsuModEasy();
+
+ if (mods.HasFlag(LegacyMods.Flashlight))
+ yield return new OsuModFlashlight();
+
+ if (mods.HasFlag(LegacyMods.HalfTime))
+ yield return new OsuModHalfTime();
+
+ if (mods.HasFlag(LegacyMods.HardRock))
+ yield return new OsuModHardRock();
+
+ if (mods.HasFlag(LegacyMods.Hidden))
+ yield return new OsuModHidden();
+
+ 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();
+ }
+
public override IEnumerable GetModsFor(ModType type)
{
switch (type)
diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
index cb5e020601..06a8e44a14 100644
--- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
@@ -12,6 +12,7 @@ using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Replays.Types;
using osu.Game.Rulesets.Taiko.Replays;
+using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Taiko
{
@@ -31,6 +32,44 @@ namespace osu.Game.Rulesets.Taiko
new KeyBinding(InputKey.MouseRight, TaikoAction.RightRim),
};
+ public override IEnumerable ConvertLegacyMods(LegacyMods mods)
+ {
+ if (mods.HasFlag(LegacyMods.Nightcore))
+ yield return new TaikoModNightcore();
+ else if (mods.HasFlag(LegacyMods.DoubleTime))
+ yield return new TaikoModDoubleTime();
+
+ if (mods.HasFlag(LegacyMods.Autoplay))
+ yield return new TaikoModAutoplay();
+
+ if (mods.HasFlag(LegacyMods.Easy))
+ yield return new TaikoModEasy();
+
+ if (mods.HasFlag(LegacyMods.Flashlight))
+ yield return new TaikoModFlashlight();
+
+ if (mods.HasFlag(LegacyMods.HalfTime))
+ yield return new TaikoModHalfTime();
+
+ if (mods.HasFlag(LegacyMods.HardRock))
+ yield return new TaikoModHardRock();
+
+ if (mods.HasFlag(LegacyMods.Hidden))
+ yield return new TaikoModHidden();
+
+ 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)
{
switch (type)
diff --git a/osu.Game.Tests/Visual/TestCaseBadgeContainer.cs b/osu.Game.Tests/Visual/TestCaseBadgeContainer.cs
new file mode 100644
index 0000000000..8177e2e272
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseBadgeContainer.cs
@@ -0,0 +1,62 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Game.Overlays.Profile.Header;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual
+{
+ [TestFixture]
+ public class TestCaseBadgeContainer : OsuTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[] { typeof(BadgeContainer) };
+
+ public TestCaseBadgeContainer()
+ {
+ BadgeContainer badgeContainer;
+
+ Child = badgeContainer = new BadgeContainer
+ {
+ RelativeSizeAxes = Axes.Both
+ };
+
+ AddStep("Show 1 badge", () => badgeContainer.ShowBadges(new[]
+ {
+ new Badge
+ {
+ AwardedAt = DateTimeOffset.Now,
+ Description = "Appreciates compasses",
+ ImageUrl = "https://assets.ppy.sh/profile-badges/mg2018-1star.png",
+ }
+ }));
+
+ AddStep("Show 2 badges", () => badgeContainer.ShowBadges(new[]
+ {
+ new Badge
+ {
+ AwardedAt = DateTimeOffset.Now,
+ Description = "Contributed to osu!lazer testing",
+ ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.png",
+ },
+ new Badge
+ {
+ AwardedAt = DateTimeOffset.Now,
+ Description = "Appreciates compasses",
+ ImageUrl = "https://assets.ppy.sh/profile-badges/mg2018-1star.png",
+ }
+ }));
+
+ AddStep("Show many badges", () => badgeContainer.ShowBadges(Enumerable.Range(1, 20).Select(i => new Badge
+ {
+ AwardedAt = DateTimeOffset.Now,
+ Description = $"Contributed to osu!lazer testing {i} times",
+ ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg",
+ }).ToArray()));
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseRankGraph.cs b/osu.Game.Tests/Visual/TestCaseRankGraph.cs
index 45f6651537..f5558620ad 100644
--- a/osu.Game.Tests/Visual/TestCaseRankGraph.cs
+++ b/osu.Game.Tests/Visual/TestCaseRankGraph.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Game.Overlays.Profile;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using OpenTK;
@@ -11,6 +10,7 @@ using System.Collections.Generic;
using System;
using NUnit.Framework;
using osu.Game.Graphics.UserInterface;
+using osu.Game.Overlays.Profile.Header;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs
index b060b9f2f8..0fdc01a974 100644
--- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs
+++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs
@@ -8,6 +8,7 @@ using NUnit.Framework;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osu.Game.Overlays.Profile;
+using osu.Game.Overlays.Profile.Header;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
@@ -23,6 +24,7 @@ namespace osu.Game.Tests.Visual
typeof(UserProfileOverlay),
typeof(RankGraph),
typeof(LineGraph),
+ typeof(BadgeContainer)
};
public TestCaseUserProfile()
@@ -53,6 +55,15 @@ namespace osu.Game.Tests.Visual
{
Mode = @"osu",
Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray()
+ },
+ Badges = new[]
+ {
+ new Badge
+ {
+ AwardedAt = DateTimeOffset.FromUnixTimeSeconds(1505741569),
+ Description = "Outstanding help by being a voluntary test subject.",
+ ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg"
+ }
}
}, false));
diff --git a/osu.Game.Tests/Visual/TestCaseWaveContainer.cs b/osu.Game.Tests/Visual/TestCaseWaveContainer.cs
new file mode 100644
index 0000000000..2163d7c3aa
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseWaveContainer.cs
@@ -0,0 +1,54 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using OpenTK;
+using OpenTK.Graphics;
+
+namespace osu.Game.Tests.Visual
+{
+ [TestFixture]
+ public class TestCaseWaveContainer : OsuTestCase
+ {
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ WaveContainer container;
+ Add(container = new WaveContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(400),
+ FirstWaveColour = colours.Red,
+ SecondWaveColour = colours.Green,
+ ThirdWaveColour = colours.Blue,
+ FourthWaveColour = colours.Pink,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black.Opacity(0.5f),
+ },
+ new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ TextSize = 20,
+ Text = @"Wave Container",
+ },
+ },
+ });
+
+ AddStep(@"show", container.Show);
+ AddStep(@"hide", container.Hide);
+ }
+ }
+}
diff --git a/osu.Game.props b/osu.Game.props
index 87edafb97f..ec859e64a5 100644
--- a/osu.Game.props
+++ b/osu.Game.props
@@ -3,6 +3,9 @@
7
+
+ ..\app.manifest
+
osu.licenseheader
diff --git a/osu.Game/Beatmaps/Legacy/LegacyMods.cs b/osu.Game/Beatmaps/Legacy/LegacyMods.cs
new file mode 100644
index 0000000000..0983610ba0
--- /dev/null
+++ b/osu.Game/Beatmaps/Legacy/LegacyMods.cs
@@ -0,0 +1,42 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+
+namespace osu.Game.Beatmaps.Legacy
+{
+ [Flags]
+ public enum LegacyMods
+ {
+ None = 0,
+ NoFail = 1 << 0,
+ Easy = 1 << 1,
+ TouchDevice = 1 << 2,
+ Hidden = 1 << 3,
+ HardRock = 1 << 4,
+ SuddenDeath = 1 << 5,
+ DoubleTime = 1 << 6,
+ Relax = 1 << 7,
+ HalfTime = 1 << 8,
+ Nightcore = 1 << 9,
+ Flashlight = 1 << 10,
+ Autoplay = 1 << 11,
+ SpunOut = 1 << 12,
+ Autopilot = 1 << 13,
+ Perfect = 1 << 14,
+ Key4 = 1 << 15,
+ Key5 = 1 << 16,
+ Key6 = 1 << 17,
+ Key7 = 1 << 18,
+ Key8 = 1 << 19,
+ FadeIn = 1 << 20,
+ Random = 1 << 21,
+ Cinema = 1 << 22,
+ Target = 1 << 23,
+ Key9 = 1 << 24,
+ KeyCoop = 1 << 25,
+ Key1 = 1 << 26,
+ Key3 = 1 << 27,
+ Key2 = 1 << 28,
+ }
+}
diff --git a/osu.Game/Beatmaps/RankStatus.cs b/osu.Game/Beatmaps/RankStatus.cs
deleted file mode 100644
index dce4f494f1..0000000000
--- a/osu.Game/Beatmaps/RankStatus.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System.ComponentModel;
-
-namespace osu.Game.Beatmaps
-{
- public enum RankStatus
- {
- Any = 7,
- [Description("Ranked & Approved")]
- RankedApproved = 0,
- Approved = 1,
- Loved = 8,
- Favourites = 2,
- [Description("Mod Requests")]
- ModRequests = 3,
- Pending = 4,
- Graveyard = 5,
- [Description("My Maps")]
- MyMaps = 6,
- }
-}
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index 1c4beb5f88..ebc674c900 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -84,6 +84,7 @@ namespace osu.Game.Configuration
Set(OsuSetting.Version, string.Empty);
Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg);
+ Set(OsuSetting.ScreenshotCaptureMenuCursor, false);
Set(OsuSetting.SelectScrollRightClick, false);
}
@@ -131,6 +132,7 @@ namespace osu.Game.Configuration
SpeedChangeVisualisation,
Skin,
ScreenshotFormat,
+ ScreenshotCaptureMenuCursor,
SelectScrollRightClick
}
}
diff --git a/osu.Game/Graphics/Containers/WaveContainer.cs b/osu.Game/Graphics/Containers/WaveContainer.cs
new file mode 100644
index 0000000000..82432c6ee7
--- /dev/null
+++ b/osu.Game/Graphics/Containers/WaveContainer.cs
@@ -0,0 +1,167 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using OpenTK.Graphics;
+
+namespace osu.Game.Graphics.Containers
+{
+ public class WaveContainer : VisibilityContainer
+ {
+ public const float APPEAR_DURATION = 800;
+ public const float DISAPPEAR_DURATION = 500;
+
+ private const Easing easing_show = Easing.OutSine;
+ private const Easing easing_hide = Easing.InSine;
+
+ private readonly Wave firstWave;
+ private readonly Wave secondWave;
+ private readonly Wave thirdWave;
+ private readonly Wave fourthWave;
+
+ private readonly Container wavesContainer;
+ private readonly Container contentContainer;
+
+ protected override Container Content => contentContainer;
+
+ public Color4 FirstWaveColour
+ {
+ get => firstWave.Colour;
+ set => firstWave.Colour = value;
+ }
+
+ public Color4 SecondWaveColour
+ {
+ get => secondWave.Colour;
+ set => secondWave.Colour = value;
+ }
+
+ public Color4 ThirdWaveColour
+ {
+ get => thirdWave.Colour;
+ set => thirdWave.Colour = value;
+ }
+
+ public Color4 FourthWaveColour
+ {
+ get => fourthWave.Colour;
+ set => fourthWave.Colour = value;
+ }
+
+ public WaveContainer()
+ {
+ Masking = true;
+
+ AddInternal(wavesContainer = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Masking = true,
+ Children = new[]
+ {
+ firstWave = new Wave
+ {
+ Rotation = 13,
+ FinalPosition = -930,
+ },
+ secondWave = new Wave
+ {
+ Origin = Anchor.TopRight,
+ Anchor = Anchor.TopRight,
+ Rotation = -7,
+ FinalPosition = -560,
+ },
+ thirdWave = new Wave
+ {
+ Rotation = 4,
+ FinalPosition = -390,
+ },
+ fourthWave = new Wave
+ {
+ Origin = Anchor.TopRight,
+ Anchor = Anchor.TopRight,
+ Rotation = -2,
+ FinalPosition = -220,
+ },
+ },
+ });
+
+ AddInternal(contentContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ });
+ }
+
+ protected override void PopIn()
+ {
+ foreach (var w in wavesContainer.Children)
+ w.State = Visibility.Visible;
+
+ this.FadeIn(100, Easing.OutQuint);
+ contentContainer.MoveToY(0, APPEAR_DURATION, Easing.OutQuint);
+
+ this.FadeIn(100, Easing.OutQuint);
+ }
+
+ protected override void PopOut()
+ {
+ this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint);
+ contentContainer.MoveToY(DrawHeight * 2f, DISAPPEAR_DURATION, Easing.In);
+
+ foreach (var w in wavesContainer.Children)
+ w.State = Visibility.Hidden;
+
+ this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint);
+ }
+
+ protected override void UpdateAfterChildren()
+ {
+ base.UpdateAfterChildren();
+
+ // This is done as an optimization, such that invisible parts of the waves
+ // are masked away, and thus do not consume fill rate.
+ wavesContainer.Height = Math.Max(0, DrawHeight - (contentContainer.DrawHeight - contentContainer.Y));
+ }
+
+ private class Wave : VisibilityContainer
+ {
+ public float FinalPosition;
+
+ protected override bool StartHidden => true;
+
+ public Wave()
+ {
+ RelativeSizeAxes = Axes.X;
+ Width = 1.5f;
+ Masking = true;
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Shadow,
+ Colour = Color4.Black.Opacity(50),
+ Radius = 20f,
+ };
+
+ Child = new Box { RelativeSizeAxes = Axes.Both };
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ // We can not use RelativeSizeAxes for Height, because the height
+ // of our parent diminishes as the content moves up.
+ Height = Parent.Parent.DrawSize.Y * 1.5f;
+ }
+
+ protected override void PopIn() => this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show);
+ protected override void PopOut() => this.MoveToY(Parent.Parent.DrawSize.Y, DISAPPEAR_DURATION, easing_hide);
+ }
+ }
+}
diff --git a/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs b/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs
index 81ae3198c7..1e56cb6052 100644
--- a/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs
+++ b/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Graphics.Cursor
///
/// Whether any cursors can be displayed.
///
- public bool CanShowCursor = true;
+ internal bool CanShowCursor = true;
public CursorContainer Cursor { get; }
public bool ProvidingUserCursor => true;
diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs
index 34d815ec14..5f57fb76b0 100644
--- a/osu.Game/Graphics/Cursor/MenuCursor.cs
+++ b/osu.Game/Graphics/Cursor/MenuCursor.cs
@@ -12,12 +12,16 @@ using osu.Framework.Input;
using osu.Game.Configuration;
using System;
using System.Diagnostics;
+using JetBrains.Annotations;
using osu.Framework.Graphics.Textures;
namespace osu.Game.Graphics.Cursor
{
public class MenuCursor : CursorContainer
{
+ private readonly IBindable screenshotCursorVisibility = new Bindable(true);
+ public override bool IsPresent => screenshotCursorVisibility.Value && base.IsPresent;
+
protected override Drawable CreateCursor() => new Cursor();
private Bindable cursorRotate;
@@ -25,6 +29,15 @@ namespace osu.Game.Graphics.Cursor
private bool startRotation;
+ [BackgroundDependencyLoader(true)]
+ private void load([NotNull] OsuConfigManager config, [CanBeNull] ScreenshotManager screenshotManager)
+ {
+ cursorRotate = config.GetBindable(OsuSetting.CursorRotation);
+
+ if (screenshotManager != null)
+ screenshotCursorVisibility.BindTo(screenshotManager.CursorVisibility);
+ }
+
protected override bool OnMouseMove(InputState state)
{
if (cursorRotate && dragging)
@@ -104,12 +117,6 @@ namespace osu.Game.Graphics.Cursor
ActiveCursor.ScaleTo(0.6f, 250, Easing.In);
}
- [BackgroundDependencyLoader]
- private void load(OsuConfigManager config)
- {
- cursorRotate = config.GetBindable(OsuSetting.CursorRotation);
- }
-
public class Cursor : Container
{
private Container cursorContainer;
diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs
index 5e0b9c9340..90580c50df 100644
--- a/osu.Game/Graphics/ScreenshotManager.cs
+++ b/osu.Game/Graphics/ScreenshotManager.cs
@@ -4,6 +4,8 @@
using System;
using System.Drawing.Imaging;
using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
@@ -12,6 +14,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Framework.Platform;
+using osu.Framework.Threading;
using osu.Game.Configuration;
using osu.Game.Input.Bindings;
using osu.Game.Overlays;
@@ -21,7 +24,17 @@ namespace osu.Game.Graphics
{
public class ScreenshotManager : Container, IKeyBindingHandler, IHandleGlobalInput
{
+ private readonly BindableBool cursorVisibility = new BindableBool(true);
+
+ ///
+ /// Changed when screenshots are being or have finished being taken, to control whether cursors should be visible.
+ /// If cursors should not be visible, cursors have 3 frames to hide themselves.
+ ///
+ public IBindable CursorVisibility => cursorVisibility;
+
private Bindable screenshotFormat;
+ private Bindable captureMenuCursor;
+
private GameHost host;
private Storage storage;
private NotificationOverlay notificationOverlay;
@@ -36,6 +49,7 @@ namespace osu.Game.Graphics
this.notificationOverlay = notificationOverlay;
screenshotFormat = config.GetBindable(OsuSetting.ScreenshotFormat);
+ captureMenuCursor = config.GetBindable(OsuSetting.ScreenshotCaptureMenuCursor);
shutter = audio.Sample.Get("UI/shutter");
}
@@ -55,10 +69,31 @@ namespace osu.Game.Graphics
public bool OnReleased(GlobalAction action) => false;
- public async void TakeScreenshotAsync()
+ private volatile int screenShotTasks;
+
+ public async Task TakeScreenshotAsync() => await Task.Run(async () =>
{
+ Interlocked.Increment(ref screenShotTasks);
+
+ if (!captureMenuCursor.Value)
+ {
+ cursorVisibility.Value = false;
+
+ // We need to wait for at most 3 draw nodes to be drawn, following which we can be assured at least one DrawNode has been generated/drawn with the set value
+ 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 bitmap = await host.TakeScreenshotAsync())
{
+ Interlocked.Decrement(ref screenShotTasks);
+
var fileName = getFileName();
if (fileName == null) return;
@@ -86,6 +121,14 @@ namespace osu.Game.Graphics
}
});
}
+ });
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (cursorVisibility == false && Interlocked.CompareExchange(ref screenShotTasks, 0, 0) == 0)
+ cursorVisibility.Value = true;
}
private string getFileName()
diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs
index e961dd9a9e..d68314f8fc 100644
--- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs
+++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs
@@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
-using osu.Game.Beatmaps;
+using System.ComponentModel;
using osu.Game.Overlays;
using osu.Game.Overlays.Direct;
using osu.Game.Rulesets;
@@ -13,21 +13,36 @@ namespace osu.Game.Online.API.Requests
{
private readonly string query;
private readonly RulesetInfo ruleset;
- private readonly RankStatus rankStatus;
+ private readonly BeatmapSearchCategory searchCategory;
private readonly DirectSortCriteria sortCriteria;
private readonly SortDirection direction;
private string directionString => direction == SortDirection.Descending ? @"desc" : @"asc";
- public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, RankStatus rankStatus = RankStatus.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending)
+ public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, BeatmapSearchCategory searchCategory = BeatmapSearchCategory.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending)
{
this.query = System.Uri.EscapeDataString(query);
this.ruleset = ruleset;
- this.rankStatus = rankStatus;
+ this.searchCategory = searchCategory;
this.sortCriteria = sortCriteria;
this.direction = direction;
}
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
- protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)rankStatus}&sort={sortCriteria.ToString().ToLower()}_{directionString}";
+ protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)searchCategory}&sort={sortCriteria.ToString().ToLower()}_{directionString}";
+ }
+
+ public enum BeatmapSearchCategory
+ {
+ Any = 7,
+ [Description("Ranked & Approved")]
+ RankedApproved = 0,
+ Approved = 1,
+ Loved = 8,
+ Favourites = 2,
+ Qualified = 3,
+ Pending = 4,
+ Graveyard = 5,
+ [Description("My Maps")]
+ MyMaps = 6,
}
}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index d1cf372067..f69135e5c9 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -60,6 +60,8 @@ namespace osu.Game
private BeatmapSetOverlay beatmapSetOverlay;
+ private ScreenshotManager screenshotManager;
+
public virtual Storage GetStorageForStableInstall() => null;
private Intro intro
@@ -194,12 +196,16 @@ namespace osu.Game
}
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap);
+ Beatmap.Value.Mods.Value = s.Mods;
menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay)));
}
protected override void LoadComplete()
{
+ // this needs to be cached before base.LoadComplete as it is used by CursorOverrideContainer.
+ dependencies.Cache(screenshotManager = new ScreenshotManager());
+
base.LoadComplete();
// The next time this is updated is in UpdateAfterChildren, which occurs too late and results
@@ -243,7 +249,8 @@ namespace osu.Game
loadComponentSingleFile(volume = new VolumeOverlay(), overlayContent.Add);
loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add);
- loadComponentSingleFile(new ScreenshotManager(), Add);
+
+ loadComponentSingleFile(screenshotManager, Add);
//overlay elements
loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add);
@@ -439,7 +446,7 @@ namespace osu.Game
sensitivity.Value = 1;
sensitivity.Disabled = true;
- frameworkConfig.Set(FrameworkSetting.ActiveInputHandlers, string.Empty);
+ frameworkConfig.Set(FrameworkSetting.IgnoredInputHandlers, string.Empty);
frameworkConfig.GetBindable(FrameworkSetting.ConfineMouseMode).SetDefault();
return true;
case GlobalAction.ToggleToolbar:
diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs
index 93e10751d9..d2cf0a6ef1 100644
--- a/osu.Game/Overlays/BeatmapSetOverlay.cs
+++ b/osu.Game/Overlays/BeatmapSetOverlay.cs
@@ -40,10 +40,10 @@ namespace osu.Game.Overlays
public BeatmapSetOverlay()
{
- FirstWaveColour = OsuColour.Gray(0.4f);
- SecondWaveColour = OsuColour.Gray(0.3f);
- ThirdWaveColour = OsuColour.Gray(0.2f);
- FourthWaveColour = OsuColour.Gray(0.1f);
+ Waves.FirstWaveColour = OsuColour.Gray(0.4f);
+ Waves.SecondWaveColour = OsuColour.Gray(0.3f);
+ Waves.ThirdWaveColour = OsuColour.Gray(0.2f);
+ Waves.FourthWaveColour = OsuColour.Gray(0.1f);
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
@@ -123,14 +123,14 @@ namespace osu.Game.Overlays
protected override void PopIn()
{
base.PopIn();
- FadeEdgeEffectTo(0.25f, APPEAR_DURATION, Easing.In);
+ FadeEdgeEffectTo(0.25f, WaveContainer.APPEAR_DURATION, Easing.In);
}
protected override void PopOut()
{
base.PopOut();
header.Details.StopPreview();
- FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out);
+ FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out);
}
protected override bool OnClick(InputState state)
diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs
index c77994efb2..8883dfdebb 100644
--- a/osu.Game/Overlays/Direct/FilterControl.cs
+++ b/osu.Game/Overlays/Direct/FilterControl.cs
@@ -7,15 +7,15 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
+using osu.Game.Online.API.Requests;
using osu.Game.Overlays.SearchableList;
using osu.Game.Rulesets;
namespace osu.Game.Overlays.Direct
{
- public class FilterControl : SearchableListFilterControl
+ public class FilterControl : SearchableListFilterControl
{
public readonly Bindable Ruleset = new Bindable();
private FillFlowContainer modeButtons;
diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs
index 9f36d5acb7..44e24d8157 100644
--- a/osu.Game/Overlays/Direct/PlayButton.cs
+++ b/osu.Game/Overlays/Direct/PlayButton.cs
@@ -173,8 +173,9 @@ namespace osu.Game.Overlays.Direct
if (trackLoader != d) return;
Preview = d?.Preview;
- Playing.TriggerChange();
+ updatePreviewTrack(Playing);
loading = false;
+
Add(trackLoader);
});
}
diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs
index 6c9433836a..f437546888 100644
--- a/osu.Game/Overlays/DirectOverlay.cs
+++ b/osu.Game/Overlays/DirectOverlay.cs
@@ -22,7 +22,7 @@ using OpenTK.Graphics;
namespace osu.Game.Overlays
{
- public class DirectOverlay : SearchableListOverlay
+ public class DirectOverlay : SearchableListOverlay
{
private const float panel_padding = 10f;
@@ -40,7 +40,7 @@ namespace osu.Game.Overlays
protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"3f5265");
protected override SearchableListHeader CreateHeader() => new Header();
- protected override SearchableListFilterControl CreateFilterControl() => new FilterControl();
+ protected override SearchableListFilterControl CreateFilterControl() => new FilterControl();
private IEnumerable beatmapSets;
@@ -89,10 +89,10 @@ namespace osu.Game.Overlays
// osu!direct colours are not part of the standard palette
- FirstWaveColour = OsuColour.FromHex(@"19b0e2");
- SecondWaveColour = OsuColour.FromHex(@"2280a2");
- ThirdWaveColour = OsuColour.FromHex(@"005774");
- FourthWaveColour = OsuColour.FromHex(@"003a4e");
+ Waves.FirstWaveColour = OsuColour.FromHex(@"19b0e2");
+ Waves.SecondWaveColour = OsuColour.FromHex(@"2280a2");
+ Waves.ThirdWaveColour = OsuColour.FromHex(@"005774");
+ Waves.FourthWaveColour = OsuColour.FromHex(@"003a4e");
ScrollFlow.Children = new Drawable[]
{
diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
index 48c38c467e..632c00d1fd 100644
--- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs
+++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
@@ -18,6 +18,7 @@ using System.Linq;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics.Containers;
using osu.Game.Rulesets;
using osu.Game.Graphics.UserInterface;
@@ -113,14 +114,14 @@ namespace osu.Game.Overlays.Mods
{
base.PopOut();
- footerContainer.MoveToX(footerContainer.DrawSize.X, DISAPPEAR_DURATION, Easing.InSine);
- footerContainer.FadeOut(DISAPPEAR_DURATION, Easing.InSine);
+ footerContainer.MoveToX(footerContainer.DrawSize.X, WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
+ footerContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
foreach (ModSection section in ModSectionsContainer.Children)
{
- section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), DISAPPEAR_DURATION, Easing.InSine);
- section.ButtonsContainer.MoveToX(100f, DISAPPEAR_DURATION, Easing.InSine);
- section.ButtonsContainer.FadeOut(DISAPPEAR_DURATION, Easing.InSine);
+ section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
+ section.ButtonsContainer.MoveToX(100f, WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
+ section.ButtonsContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
}
}
@@ -128,14 +129,14 @@ namespace osu.Game.Overlays.Mods
{
base.PopIn();
- footerContainer.MoveToX(0, APPEAR_DURATION, Easing.OutQuint);
- footerContainer.FadeIn(APPEAR_DURATION, Easing.OutQuint);
+ footerContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint);
+ footerContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint);
foreach (ModSection section in ModSectionsContainer.Children)
{
- section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), APPEAR_DURATION, Easing.OutQuint);
- section.ButtonsContainer.MoveToX(0, APPEAR_DURATION, Easing.OutQuint);
- section.ButtonsContainer.FadeIn(APPEAR_DURATION, Easing.OutQuint);
+ section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), WaveContainer.APPEAR_DURATION, Easing.OutQuint);
+ section.ButtonsContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint);
+ section.ButtonsContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint);
}
}
@@ -181,14 +182,12 @@ namespace osu.Game.Overlays.Mods
public ModSelectOverlay()
{
- FirstWaveColour = OsuColour.FromHex(@"19b0e2");
- SecondWaveColour = OsuColour.FromHex(@"2280a2");
- ThirdWaveColour = OsuColour.FromHex(@"005774");
- FourthWaveColour = OsuColour.FromHex(@"003a4e");
+ Waves.FirstWaveColour = OsuColour.FromHex(@"19b0e2");
+ Waves.SecondWaveColour = OsuColour.FromHex(@"2280a2");
+ Waves.ThirdWaveColour = OsuColour.FromHex(@"005774");
+ Waves.FourthWaveColour = OsuColour.FromHex(@"003a4e");
Height = 510;
- Content.RelativeSizeAxes = Axes.X;
- Content.AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
new Container
diff --git a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs
new file mode 100644
index 0000000000..291db45e97
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs
@@ -0,0 +1,195 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using osu.Framework.Input;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Users;
+using OpenTK;
+
+namespace osu.Game.Overlays.Profile.Header
+{
+ public class BadgeContainer : Container
+ {
+ private static readonly Vector2 badge_size = new Vector2(86, 40);
+ private static readonly MarginPadding outer_padding = new MarginPadding(3);
+
+ private OsuSpriteText badgeCountText;
+ private FillFlowContainer badgeFlowContainer;
+ private FillFlowContainer outerBadgeContainer;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Child = new Container
+ {
+ Masking = true,
+ CornerRadius = 4,
+ AutoSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = colours.Gray3
+ },
+ outerBadgeContainer = new OuterBadgeContainer(onOuterHover, onOuterHoverLost)
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Direction = FillDirection.Vertical,
+ Padding = outer_padding,
+ Width = DrawableBadge.DRAWABLE_BADGE_SIZE.X + outer_padding.TotalHorizontal,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ badgeCountText = new OsuSpriteText
+ {
+ Alpha = 0,
+ TextSize = 12,
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ Font = "Exo2.0-Regular"
+ },
+ new Container
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ AutoSizeAxes = Axes.Both,
+ Child = badgeFlowContainer = new FillFlowContainer
+ {
+ Direction = FillDirection.Horizontal,
+ AutoSizeAxes = Axes.Both,
+ }
+ }
+ }
+ },
+ }
+ };
+
+ Scheduler.AddDelayed(rotateBadges, 3000, true);
+ }
+
+ private void rotateBadges()
+ {
+ if (outerBadgeContainer.IsHovered) return;
+
+ visibleBadge = (visibleBadge + 1) % badgeCount;
+
+ badgeFlowContainer.MoveToX(-DrawableBadge.DRAWABLE_BADGE_SIZE.X * visibleBadge, 500, Easing.InOutQuad);
+ }
+
+ private int visibleBadge;
+ private int badgeCount;
+
+ public void ShowBadges(Badge[] badges)
+ {
+ switch (badges.Length)
+ {
+ case 0:
+ Hide();
+ return;
+ case 1:
+ badgeCountText.Hide();
+ break;
+ default:
+ badgeCountText.Show();
+ badgeCountText.Text = $"{badges.Length} badges";
+ break;
+ }
+
+ Show();
+ badgeCount = badges.Length;
+ visibleBadge = 0;
+
+ badgeFlowContainer.Clear();
+ foreach (var badge in badges)
+ {
+ LoadComponentAsync(new DrawableBadge(badge)
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ }, badgeFlowContainer.Add);
+ }
+ }
+
+ private void onOuterHover()
+ {
+ badgeFlowContainer.ClearTransforms();
+ badgeFlowContainer.X = 0;
+ badgeFlowContainer.Direction = FillDirection.Full;
+ outerBadgeContainer.AutoSizeAxes = Axes.Both;
+
+ badgeFlowContainer.MaximumSize = new Vector2(ChildSize.X, float.MaxValue);
+ }
+
+ private void onOuterHoverLost()
+ {
+ badgeFlowContainer.X = -DrawableBadge.DRAWABLE_BADGE_SIZE.X * visibleBadge;
+ badgeFlowContainer.Direction = FillDirection.Horizontal;
+ outerBadgeContainer.AutoSizeAxes = Axes.Y;
+ outerBadgeContainer.Width = DrawableBadge.DRAWABLE_BADGE_SIZE.X + outer_padding.TotalHorizontal;
+ }
+
+ private class OuterBadgeContainer : FillFlowContainer
+ {
+ private readonly Action hoverAction;
+ private readonly Action hoverLostAction;
+
+ public OuterBadgeContainer(Action hoverAction, Action hoverLostAction)
+ {
+ this.hoverAction = hoverAction;
+ this.hoverLostAction = hoverLostAction;
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ hoverAction();
+ return true;
+ }
+
+ protected override void OnHoverLost(InputState state) => hoverLostAction();
+ }
+
+ private class DrawableBadge : Container, IHasTooltip
+ {
+ public static readonly Vector2 DRAWABLE_BADGE_SIZE = badge_size + outer_padding.Total;
+
+ private readonly Badge badge;
+
+ public DrawableBadge(Badge badge)
+ {
+ this.badge = badge;
+ Padding = outer_padding;
+ Size = DRAWABLE_BADGE_SIZE;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ Child = new Sprite
+ {
+ FillMode = FillMode.Fit,
+ RelativeSizeAxes = Axes.Both,
+ Texture = textures.Get(badge.ImageUrl),
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ Child.FadeInFromZero(200);
+ }
+
+ public string TooltipText => badge.Description;
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/RankGraph.cs b/osu.Game/Overlays/Profile/Header/RankGraph.cs
similarity index 99%
rename from osu.Game/Overlays/Profile/RankGraph.cs
rename to osu.Game/Overlays/Profile/Header/RankGraph.cs
index 72dd4352f6..2c70507536 100644
--- a/osu.Game/Overlays/Profile/RankGraph.cs
+++ b/osu.Game/Overlays/Profile/Header/RankGraph.cs
@@ -2,9 +2,10 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
+using System.Collections.Generic;
using System.Linq;
-using OpenTK;
using osu.Framework.Allocation;
+using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -14,10 +15,9 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Users;
-using System.Collections.Generic;
-using osu.Framework.Configuration;
+using OpenTK;
-namespace osu.Game.Overlays.Profile
+namespace osu.Game.Overlays.Profile.Header
{
public class RankGraph : Container
{
diff --git a/osu.Game/Overlays/Profile/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/SupporterIcon.cs
similarity index 98%
rename from osu.Game/Overlays/Profile/SupporterIcon.cs
rename to osu.Game/Overlays/Profile/Header/SupporterIcon.cs
index e8d52bf50e..37ad63464c 100644
--- a/osu.Game/Overlays/Profile/SupporterIcon.cs
+++ b/osu.Game/Overlays/Profile/Header/SupporterIcon.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -9,8 +8,9 @@ using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
+using OpenTK;
-namespace osu.Game.Overlays.Profile
+namespace osu.Game.Overlays.Profile.Header
{
public class SupporterIcon : CircularContainer, IHasTooltip
{
diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs
index 4a88431cc4..ec0e45d5ca 100644
--- a/osu.Game/Overlays/Profile/ProfileHeader.cs
+++ b/osu.Game/Overlays/Profile/ProfileHeader.cs
@@ -17,6 +17,7 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
+using osu.Game.Overlays.Profile.Header;
using osu.Game.Users;
namespace osu.Game.Overlays.Profile
@@ -35,6 +36,7 @@ namespace osu.Game.Overlays.Profile
private readonly GradeBadge gradeSSPlus, gradeSS, gradeSPlus, gradeS, gradeA;
private readonly Box colourBar;
private readonly DrawableFlag countryFlag;
+ private readonly BadgeContainer badgeContainer;
private const float cover_height = 350;
private const float info_height = 150;
@@ -42,6 +44,7 @@ namespace osu.Game.Overlays.Profile
private const float avatar_size = 110;
private const float level_position = 30;
private const float level_height = 60;
+ private const float stats_width = 280;
public ProfileHeader(User user)
{
@@ -66,9 +69,9 @@ namespace osu.Game.Overlays.Profile
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
- X = UserProfileOverlay.CONTENT_X_MARGIN,
- Y = -20,
- AutoSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN, Bottom = 20, Right = stats_width + UserProfileOverlay.CONTENT_X_MARGIN },
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
Children = new Drawable[]
{
new UpdateableAvatar
@@ -116,7 +119,15 @@ namespace osu.Game.Overlays.Profile
Height = 20
}
}
- }
+ },
+ badgeContainer = new BadgeContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Origin = Anchor.BottomLeft,
+ Margin = new MarginPadding { Bottom = 5 },
+ Alpha = 0,
+ },
}
},
colourBar = new Box
@@ -156,7 +167,7 @@ namespace osu.Game.Overlays.Profile
{
X = -UserProfileOverlay.CONTENT_X_MARGIN,
RelativeSizeAxes = Axes.Y,
- Width = 280,
+ Width = stats_width,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Children = new Drawable[]
@@ -417,6 +428,8 @@ namespace osu.Game.Overlays.Profile
rankGraph.User.Value = user;
}
+
+ badgeContainer.ShowBadges(user.Badges);
}
private void tryAddInfoRightLine(FontAwesome icon, string str, string url = null)
diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs
index 842a13f8da..3fec9d8697 100644
--- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs
+++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using OpenTK;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
@@ -19,6 +20,8 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
private DirectPanel currentlyPlaying;
+ public event Action BeganPlayingPreview;
+
public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, string header, string missing = "None... yet.")
: base(user, header, missing)
{
@@ -56,17 +59,25 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
panel.PreviewPlaying.ValueChanged += isPlaying =>
{
- if (!isPlaying) return;
+ StopPlayingPreview();
- if (currentlyPlaying != null && currentlyPlaying != panel)
- currentlyPlaying.PreviewPlaying.Value = false;
-
- currentlyPlaying = panel;
+ if (isPlaying)
+ {
+ BeganPlayingPreview?.Invoke(this);
+ currentlyPlaying = panel;
+ }
};
}
};
Api.Queue(req);
}
+
+ public void StopPlayingPreview()
+ {
+ if (currentlyPlaying == null) return;
+ currentlyPlaying.PreviewPlaying.Value = false;
+ currentlyPlaying = null;
+ }
}
}
diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs
index 367d096c16..92abd20f93 100644
--- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs
+++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.Linq;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile.Sections.Beatmaps;
@@ -21,6 +22,15 @@ namespace osu.Game.Overlays.Profile.Sections
new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, "Pending Beatmaps"),
new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, "Graveyarded Beatmaps"),
};
+
+ foreach (var paginatedBeatmapContainer in Children.OfType())
+ {
+ paginatedBeatmapContainer.BeganPlayingPreview += _ =>
+ {
+ foreach (var bc in Children.OfType())
+ bc.StopPlayingPreview();
+ };
+ }
}
}
}
diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs
index a78cb29468..54049bfb1f 100644
--- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs
@@ -30,6 +30,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
{
LabelText = "Screenshot format",
Bindable = config.GetBindable(OsuSetting.ScreenshotFormat)
+ },
+ new SettingsCheckbox
+ {
+ LabelText = "Show menu cursor in screenshots",
+ Bindable = config.GetBindable(OsuSetting.ScreenshotCaptureMenuCursor)
}
};
}
diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
index c4fced922f..51a624330b 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
@@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
protected override string Header => "Mouse";
private readonly BindableBool rawInputToggle = new BindableBool();
- private Bindable activeInputHandlers;
+ private Bindable ignoredInputHandler;
private SensitivitySetting sensitivity;
[BackgroundDependencyLoader]
@@ -61,20 +61,18 @@ namespace osu.Game.Overlays.Settings.Sections.Input
const string raw_mouse_handler = @"OpenTKRawMouseHandler";
const string standard_mouse_handler = @"OpenTKMouseHandler";
- activeInputHandlers.Value = enabled ?
- activeInputHandlers.Value.Replace(standard_mouse_handler, raw_mouse_handler) :
- activeInputHandlers.Value.Replace(raw_mouse_handler, standard_mouse_handler);
+ ignoredInputHandler.Value = enabled ? standard_mouse_handler : raw_mouse_handler;
};
- activeInputHandlers = config.GetBindable(FrameworkSetting.ActiveInputHandlers);
- activeInputHandlers.ValueChanged += handlers =>
+ ignoredInputHandler = config.GetBindable(FrameworkSetting.IgnoredInputHandlers);
+ ignoredInputHandler.ValueChanged += handler =>
{
- bool raw = handlers.Contains("Raw");
+ bool raw = !handler.Contains("Raw");
rawInputToggle.Value = raw;
sensitivity.Bindable.Disabled = !raw;
};
- activeInputHandlers.TriggerChange();
+ ignoredInputHandler.TriggerChange();
}
private class SensitivitySetting : SettingsSlider
diff --git a/osu.Game/Overlays/SocialOverlay.cs b/osu.Game/Overlays/SocialOverlay.cs
index d224d4c92d..222035ab65 100644
--- a/osu.Game/Overlays/SocialOverlay.cs
+++ b/osu.Game/Overlays/SocialOverlay.cs
@@ -48,10 +48,10 @@ namespace osu.Game.Overlays
public SocialOverlay()
{
- FirstWaveColour = OsuColour.FromHex(@"cb5fa0");
- SecondWaveColour = OsuColour.FromHex(@"b04384");
- ThirdWaveColour = OsuColour.FromHex(@"9b2b6e");
- FourthWaveColour = OsuColour.FromHex(@"6d214d");
+ Waves.FirstWaveColour = OsuColour.FromHex(@"cb5fa0");
+ Waves.SecondWaveColour = OsuColour.FromHex(@"b04384");
+ Waves.ThirdWaveColour = OsuColour.FromHex(@"9b2b6e");
+ Waves.FourthWaveColour = OsuColour.FromHex(@"6d214d");
Add(loading = new LoadingAnimation());
diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs
index c6e8c60f92..a4dd0c9ec3 100644
--- a/osu.Game/Overlays/UserProfileOverlay.cs
+++ b/osu.Game/Overlays/UserProfileOverlay.cs
@@ -35,10 +35,10 @@ namespace osu.Game.Overlays
public UserProfileOverlay()
{
- FirstWaveColour = OsuColour.Gray(0.4f);
- SecondWaveColour = OsuColour.Gray(0.3f);
- ThirdWaveColour = OsuColour.Gray(0.2f);
- FourthWaveColour = OsuColour.Gray(0.1f);
+ Waves.FirstWaveColour = OsuColour.Gray(0.4f);
+ Waves.SecondWaveColour = OsuColour.Gray(0.3f);
+ Waves.ThirdWaveColour = OsuColour.Gray(0.2f);
+ Waves.FourthWaveColour = OsuColour.Gray(0.1f);
RelativeSizeAxes = Axes.Both;
RelativePositionAxes = Axes.Both;
@@ -64,13 +64,13 @@ namespace osu.Game.Overlays
protected override void PopIn()
{
base.PopIn();
- FadeEdgeEffectTo(0.5f, APPEAR_DURATION, Easing.In);
+ FadeEdgeEffectTo(0.5f, WaveContainer.APPEAR_DURATION, Easing.In);
}
protected override void PopOut()
{
base.PopOut();
- FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out);
+ FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out);
}
public void ShowUser(long userId)
diff --git a/osu.Game/Overlays/WaveOverlayContainer.cs b/osu.Game/Overlays/WaveOverlayContainer.cs
index 6a083a77e5..97f52d88f7 100644
--- a/osu.Game/Overlays/WaveOverlayContainer.cs
+++ b/osu.Game/Overlays/WaveOverlayContainer.cs
@@ -1,203 +1,37 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using OpenTK.Graphics;
-using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
-using System;
-using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays
{
public abstract class WaveOverlayContainer : OsuFocusedOverlayContainer
{
- protected const float APPEAR_DURATION = 800;
- protected const float DISAPPEAR_DURATION = 500;
-
- private const Easing easing_show = Easing.OutSine;
- private const Easing easing_hide = Easing.InSine;
-
- private readonly Wave firstWave;
- private readonly Wave secondWave;
- private readonly Wave thirdWave;
- private readonly Wave fourthWave;
-
- private readonly Container wavesContainer;
-
- private readonly Container contentContainer;
+ protected readonly WaveContainer Waves;
protected override bool BlockPassThroughKeyboard => true;
-
- protected override Container Content => contentContainer;
-
- protected Color4 FirstWaveColour
- {
- get
- {
- return firstWave.Colour;
- }
- set
- {
- if (firstWave.Colour == value) return;
- firstWave.Colour = value;
- }
- }
-
- protected Color4 SecondWaveColour
- {
- get
- {
- return secondWave.Colour;
- }
- set
- {
- if (secondWave.Colour == value) return;
- secondWave.Colour = value;
- }
- }
-
- protected Color4 ThirdWaveColour
- {
- get
- {
- return thirdWave.Colour;
- }
- set
- {
- if (thirdWave.Colour == value) return;
- thirdWave.Colour = value;
- }
- }
-
- protected Color4 FourthWaveColour
- {
- get
- {
- return fourthWave.Colour;
- }
- set
- {
- if (fourthWave.Colour == value) return;
- fourthWave.Colour = value;
- }
- }
+ protected override Container Content => Waves;
protected WaveOverlayContainer()
{
- Masking = true;
-
- AddInternal(wavesContainer = new Container
- {
- RelativeSizeAxes = Axes.X,
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Masking = true,
- Children = new[]
- {
- firstWave = new Wave
- {
- Rotation = 13,
- FinalPosition = -930,
- },
- secondWave = new Wave
- {
- Origin = Anchor.TopRight,
- Anchor = Anchor.TopRight,
- Rotation = -7,
- FinalPosition = -560,
- },
- thirdWave = new Wave
- {
- Rotation = 4,
- FinalPosition = -390,
- },
- fourthWave = new Wave
- {
- Origin = Anchor.TopRight,
- Anchor = Anchor.TopRight,
- Rotation = -2,
- FinalPosition = -220,
- },
- },
- });
-
- AddInternal(contentContainer = new Container
+ AddInternal(Waves = new WaveContainer
{
RelativeSizeAxes = Axes.Both,
- Anchor = Anchor.BottomCentre,
- Origin = Anchor.BottomCentre,
});
}
protected override void PopIn()
{
base.PopIn();
-
- foreach (var w in wavesContainer.Children)
- w.State = Visibility.Visible;
-
- this.FadeIn(100, Easing.OutQuint);
- contentContainer.MoveToY(0, APPEAR_DURATION, Easing.OutQuint);
-
- this.FadeIn(100, Easing.OutQuint);
+ Waves.Show();
}
protected override void PopOut()
{
base.PopOut();
-
- this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint);
- contentContainer.MoveToY(DrawHeight * 2f, DISAPPEAR_DURATION, Easing.In);
-
- foreach (var w in wavesContainer.Children)
- w.State = Visibility.Hidden;
-
- this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint);
- }
-
- protected override void UpdateAfterChildren()
- {
- base.UpdateAfterChildren();
-
- // This is done as an optimization, such that invisible parts of the waves
- // are masked away, and thus do not consume fill rate.
- wavesContainer.Height = Math.Max(0, DrawHeight - (contentContainer.DrawHeight - contentContainer.Y));
- }
-
- private class Wave : VisibilityContainer
- {
- public float FinalPosition;
-
- protected override bool StartHidden => true;
-
- public Wave()
- {
- RelativeSizeAxes = Axes.X;
- Width = 1.5f;
- Masking = true;
- EdgeEffect = new EdgeEffectParameters
- {
- Type = EdgeEffectType.Shadow,
- Colour = Color4.Black.Opacity(50),
- Radius = 20f,
- };
-
- Child = new Box { RelativeSizeAxes = Axes.Both };
- }
-
- protected override void Update()
- {
- base.Update();
-
- // We can not use RelativeSizeAxes for Height, because the height
- // of our parent diminishes as the content moves up.
- Height = Parent.Parent.DrawSize.Y * 1.5f;
- }
-
- protected override void PopIn() => this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show);
- protected override void PopOut() => this.MoveToY(Parent.Parent.DrawSize.Y, DISAPPEAR_DURATION, easing_hide);
+ Waves.Hide();
}
}
}
diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs
index c2af4d566c..cd1d030afe 100644
--- a/osu.Game/Rulesets/Ruleset.cs
+++ b/osu.Game/Rulesets/Ruleset.cs
@@ -14,6 +14,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Replays.Types;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
+using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets
{
@@ -33,6 +34,13 @@ namespace osu.Game.Rulesets
public abstract IEnumerable GetModsFor(ModType type);
+ ///
+ /// Converts mods from legacy enum values. Do not override if you're not a legacy ruleset.
+ ///
+ /// The legacy enum which will be converted
+ /// An enumerable of constructed s
+ public virtual IEnumerable ConvertLegacyMods(LegacyMods mods) => new Mod[] { };
+
public Mod GetAutoplayMod() => GetAllMods().First(mod => mod is ModAutoplay);
protected Ruleset(RulesetInfo rulesetInfo = null)
diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs
index 5ee009ba98..239f200e29 100644
--- a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs
+++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs
@@ -9,6 +9,8 @@ using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Replays.Legacy;
using osu.Game.Users;
using SharpCompress.Compressors.LZMA;
+using osu.Game.Beatmaps.Legacy;
+using System.Linq;
namespace osu.Game.Rulesets.Scoring.Legacy
{
@@ -64,7 +66,7 @@ namespace osu.Game.Rulesets.Scoring.Legacy
/* score.Perfect = */
sr.ReadBoolean();
/* score.EnabledMods = (Mods)*/
- sr.ReadInt32();
+ score.Mods = currentRuleset.ConvertLegacyMods((LegacyMods)sr.ReadInt32()).ToArray();
/* score.HpGraphString = */
sr.ReadString();
/* score.Date = */
diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs
index 33e423a558..542ddd2c92 100644
--- a/osu.Game/Screens/Menu/Button.cs
+++ b/osu.Game/Screens/Menu/Button.cs
@@ -222,7 +222,7 @@ namespace osu.Game.Screens.Menu
boxHoverLayer.FadeOut(800, Easing.OutExpo);
}
- public override bool HandleKeyboardInput => state != ButtonState.Exploded;
+ public override bool HandleKeyboardInput => state == ButtonState.Expanded;
public override bool HandleMouseInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f;
protected override void Update()
diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
index 3a6ab8f84b..9dae8fb273 100644
--- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
+++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
@@ -39,8 +39,9 @@ namespace osu.Game.Screens.Select.Leaderboards
private readonly LoadingAnimation loading;
- private IEnumerable scores;
+ private ScheduledDelegate showScoresDelegate;
+ private IEnumerable scores;
public IEnumerable Scores
{
get { return scores; }
@@ -59,29 +60,34 @@ namespace osu.Game.Screens.Select.Leaderboards
// ensure placeholder is hidden when displaying scores
PlaceholderState = PlaceholderState.Successful;
- // schedule because we may not be loaded yet (LoadComponentAsync complains).
- Schedule(() =>
+ var flow = scrollFlow = new FillFlowContainer
{
- LoadComponentAsync(new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Spacing = new Vector2(0f, 5f),
- Padding = new MarginPadding { Top = 10, Bottom = 5 },
- ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) })
- }, f =>
- {
- scrollContainer.Add(scrollFlow = f);
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Spacing = new Vector2(0f, 5f),
+ Padding = new MarginPadding { Top = 10, Bottom = 5 },
+ ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) })
+ };
- int i = 0;
- foreach (var s in f.Children)
- {
- using (s.BeginDelayedSequence(i++ * 50, true))
- s.Show();
- }
+ // schedule because we may not be loaded yet (LoadComponentAsync complains).
+ showScoresDelegate?.Cancel();
+ if (!IsLoaded)
+ showScoresDelegate = Schedule(showScores);
+ else
+ showScores();
- scrollContainer.ScrollTo(0f, false);
- });
+ void showScores() => LoadComponentAsync(flow, _ =>
+ {
+ scrollContainer.Add(flow);
+
+ int i = 0;
+ foreach (var s in flow.Children)
+ {
+ using (s.BeginDelayedSequence(i++ * 50, true))
+ s.Show();
+ }
+
+ scrollContainer.ScrollTo(0f, false);
});
}
}
@@ -103,6 +109,10 @@ namespace osu.Game.Screens.Select.Leaderboards
private PlaceholderState placeholderState;
+ ///
+ /// Update the placeholder visibility.
+ /// Setting this to anything other than PlaceholderState.Successful will cancel all existing retrieval requests and hide scores.
+ ///
protected PlaceholderState PlaceholderState
{
get { return placeholderState; }
@@ -250,43 +260,45 @@ namespace osu.Game.Screens.Select.Leaderboards
loading.Show();
getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value ?? Beatmap.Ruleset, Scope);
- getScoresRequest.Success += r =>
+ getScoresRequest.Success += r => Schedule(() =>
{
Scores = r.Scores;
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
- };
- getScoresRequest.Failure += onUpdateFailed;
+ });
+
+ getScoresRequest.Failure += e => Schedule(() =>
+ {
+ if (e is OperationCanceledException)
+ return;
+
+ PlaceholderState = PlaceholderState.NetworkFailure;
+ Logger.Error(e, @"Couldn't fetch beatmap scores!");
+ });
api.Queue(getScoresRequest);
}
- private void onUpdateFailed(Exception e)
- {
- if (e is OperationCanceledException)
- return;
-
- PlaceholderState = PlaceholderState.NetworkFailure;
- Logger.Error(e, @"Couldn't fetch beatmap scores!");
- }
+ private Placeholder currentPlaceholder;
private void replacePlaceholder(Placeholder placeholder)
{
- var existingPlaceholder = placeholderContainer.Children.LastOrDefault() as Placeholder;
-
- if (placeholder != null && placeholder.Equals(existingPlaceholder))
+ if (placeholder != null && placeholder.Equals(currentPlaceholder))
return;
- existingPlaceholder?.FadeOut(150, Easing.OutQuint).Expire();
+ currentPlaceholder?.FadeOut(150, Easing.OutQuint).Expire();
if (placeholder == null)
+ {
+ currentPlaceholder = null;
return;
+ }
- Scores = null;
-
- placeholderContainer.Add(placeholder);
+ placeholderContainer.Child = placeholder;
placeholder.ScaleTo(0.8f).Then().ScaleTo(1, fade_duration * 3, Easing.OutQuint);
placeholder.FadeInFromZero(fade_duration, Easing.OutQuint);
+
+ currentPlaceholder = placeholder;
}
protected override void Update()
diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs
index decf0c9bdb..02f425c296 100644
--- a/osu.Game/Tests/Visual/OsuTestCase.cs
+++ b/osu.Game/Tests/Visual/OsuTestCase.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System;
using System.IO;
using System.Reflection;
using osu.Framework.Testing;
@@ -10,28 +9,27 @@ namespace osu.Game.Tests.Visual
{
public abstract class OsuTestCase : TestCase
{
- public override void RunTest()
- {
- using (var host = new CleanRunHeadlessGameHost($"test-{Guid.NewGuid()}", realtime: false))
- host.Run(new OsuTestCaseTestRunner(this));
- }
+ protected override ITestCaseTestRunner CreateRunner() => new OsuTestCaseTestRunner();
- public class OsuTestCaseTestRunner : OsuGameBase
+ public class OsuTestCaseTestRunner : OsuGameBase, ITestCaseTestRunner
{
- private readonly OsuTestCase testCase;
-
protected override string MainResourceFile => File.Exists(base.MainResourceFile) ? base.MainResourceFile : Assembly.GetExecutingAssembly().Location;
- public OsuTestCaseTestRunner(OsuTestCase testCase)
+ private readonly TestCaseTestRunner.TestRunner runner;
+
+ public OsuTestCaseTestRunner()
{
- this.testCase = testCase;
+ runner = new TestCaseTestRunner.TestRunner();
}
protected override void LoadComplete()
{
base.LoadComplete();
- Add(new TestCaseTestRunner.TestRunner(testCase));
+
+ Add(runner);
}
+
+ public void RunTestBlocking(TestCase test) => runner.RunTestBlocking(test);
}
}
}
diff --git a/osu.Game/Users/Badge.cs b/osu.Game/Users/Badge.cs
new file mode 100644
index 0000000000..25ef8ffdf4
--- /dev/null
+++ b/osu.Game/Users/Badge.cs
@@ -0,0 +1,20 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using Newtonsoft.Json;
+
+namespace osu.Game.Users
+{
+ public class Badge
+ {
+ [JsonProperty("awarded_at")]
+ public DateTimeOffset AwardedAt;
+
+ [JsonProperty("description")]
+ public string Description;
+
+ [JsonProperty("image_url")]
+ public string ImageUrl;
+ }
+}
diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs
index 441e4ffd28..b983b639f0 100644
--- a/osu.Game/Users/User.cs
+++ b/osu.Game/Users/User.cs
@@ -137,6 +137,9 @@ namespace osu.Game.Users
[JsonProperty(@"rankHistory")]
public RankHistoryData RankHistory;
+ [JsonProperty("badges")]
+ public Badge[] Badges;
+
public override string ToString() => Username;
}
}
diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs
index 424d7b7a8f..bcb91c1955 100644
--- a/osu.Game/Users/UserPanel.cs
+++ b/osu.Game/Users/UserPanel.cs
@@ -17,7 +17,7 @@ using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Cursor;
using osu.Game.Graphics.Containers;
-using osu.Game.Overlays.Profile;
+using osu.Game.Overlays.Profile.Header;
namespace osu.Game.Users
{
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 367c3490d6..1a75f1979a 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -20,7 +20,7 @@
-
+
\ No newline at end of file
diff --git a/osu.TestProject.props b/osu.TestProject.props
index cb841f1940..afdf895eac 100644
--- a/osu.TestProject.props
+++ b/osu.TestProject.props
@@ -15,7 +15,7 @@
-
+