diff --git a/osu-framework b/osu-framework
index 2bd341b29d..e24d24ae70 160000
--- a/osu-framework
+++ b/osu-framework
@@ -1 +1 @@
-Subproject commit 2bd341b29d6a7ed864aa9c1c5fad4668dafe03a4
+Subproject commit e24d24ae70a78cea5a11635c37d2808d29233e96
diff --git a/osu-resources b/osu-resources
index f6042e1cb3..a4418111f8 160000
--- a/osu-resources
+++ b/osu-resources
@@ -1 +1 @@
-Subproject commit f6042e1cb37cfad6c879d0e1245f7880c7fcd5f5
+Subproject commit a4418111f8ed2350a6fd46fe69258884f0757745
diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
index c6474eae5a..c090342a4b 100644
--- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
+++ b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
@@ -9,7 +9,7 @@
Properties
osu.Desktop.Deploy
osu.Desktop.Deploy
- v4.5.2
+ v4.6.1
512
true
diff --git a/osu.Desktop.Tests/Visual/OsuTestCase.cs b/osu.Desktop.Tests/Visual/OsuTestCase.cs
index e366aecb21..1b48ded4e2 100644
--- a/osu.Desktop.Tests/Visual/OsuTestCase.cs
+++ b/osu.Desktop.Tests/Visual/OsuTestCase.cs
@@ -1,17 +1,14 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using NUnit.Framework;
using osu.Framework.Desktop.Platform;
using osu.Framework.Testing;
using osu.Game;
namespace osu.Desktop.Tests.Visual
{
- [TestFixture]
public abstract class OsuTestCase : TestCase
{
- [Test]
public override void RunTest()
{
using (var host = new HeadlessGameHost(realtime: false))
diff --git a/osu.Desktop.Tests/Visual/TestCaseBeatmapDetails.cs b/osu.Desktop.Tests/Visual/TestCaseBeatmapDetails.cs
index 11a15cf56f..d0f631201a 100644
--- a/osu.Desktop.Tests/Visual/TestCaseBeatmapDetails.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseBeatmapDetails.cs
@@ -12,50 +12,104 @@ namespace osu.Desktop.Tests.Visual
{
public override string Description => "BeatmapDetails tab of BeatmapDetailArea";
- private readonly BeatmapDetails details;
-
public TestCaseBeatmapDetails()
{
+ BeatmapDetails details;
Add(details = new BeatmapDetails
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(150),
- Beatmap = new BeatmapInfo
+ });
+
+ AddStep("beatmap all metrics", () => details.Beatmap = new BeatmapInfo
+ {
+ Version = "All Metrics",
+ Metadata = new BeatmapMetadata
{
- Version = "VisualTest",
- Metadata = new BeatmapMetadata
- {
- Source = "Some guy",
- Tags = "beatmap metadata example with a very very long list of tags and not much creativity",
- },
- Difficulty = new BeatmapDifficulty
- {
- CircleSize = 7,
- ApproachRate = 3.5f,
- OverallDifficulty = 5.7f,
- DrainRate = 1,
- },
- StarDifficulty = 5.3f,
- Metrics = new BeatmapMetrics
- {
- Ratings = Enumerable.Range(0, 10),
- Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6),
- Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6),
- },
+ Source = "osu!lazer",
+ Tags = "this beatmap has all the metrics",
+ },
+ Difficulty = new BeatmapDifficulty
+ {
+ CircleSize = 7,
+ DrainRate = 1,
+ OverallDifficulty = 5.7f,
+ ApproachRate = 3.5f,
+ },
+ StarDifficulty = 5.3f,
+ Metrics = new BeatmapMetrics
+ {
+ Ratings = Enumerable.Range(0, 10),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
},
});
- AddRepeatStep("fail values", newRetryAndFailValues, 10);
- }
+ AddStep("beatmap ratings", () => details.Beatmap = new BeatmapInfo
+ {
+ Version = "Only Ratings",
+ Metadata = new BeatmapMetadata
+ {
+ Source = "osu!lazer",
+ Tags = "this beatmap has ratings metrics but not retries or fails",
+ },
+ Difficulty = new BeatmapDifficulty
+ {
+ CircleSize = 6,
+ DrainRate = 9,
+ OverallDifficulty = 6,
+ ApproachRate = 6,
+ },
+ StarDifficulty = 4.8f,
+ Metrics = new BeatmapMetrics
+ {
+ Ratings = Enumerable.Range(0, 10),
+ },
+ });
- private int lastRange = 1;
+ AddStep("beatmap fails retries", () => details.Beatmap = new BeatmapInfo
+ {
+ Version = "Only Retries and Fails",
+ Metadata = new BeatmapMetadata
+ {
+ Source = "osu!lazer",
+ Tags = "this beatmap has retries and fails but no ratings",
+ },
+ Difficulty = new BeatmapDifficulty
+ {
+ CircleSize = 3.7f,
+ DrainRate = 6,
+ OverallDifficulty = 6,
+ ApproachRate = 7,
+ },
+ StarDifficulty = 2.91f,
+ Metrics = new BeatmapMetrics
+ {
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ },
+ });
- private void newRetryAndFailValues()
- {
- details.Beatmap.Metrics.Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6);
- details.Beatmap.Metrics.Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6);
- details.Beatmap = details.Beatmap;
- lastRange += 100;
+ AddStep("beatmap no metrics", () => details.Beatmap = new BeatmapInfo
+ {
+ Version = "No Metrics",
+ Metadata = new BeatmapMetadata
+ {
+ Source = "osu!lazer",
+ Tags = "this beatmap has no metrics",
+ },
+ Difficulty = new BeatmapDifficulty
+ {
+ CircleSize = 5,
+ DrainRate = 5,
+ OverallDifficulty = 5.5f,
+ ApproachRate = 6.5f,
+ },
+ StarDifficulty = 1.97f,
+ Metrics = new BeatmapMetrics(),
+ });
+
+ AddStep("null beatmap", () => details.Beatmap = null);
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Desktop.Tests/Visual/TestCaseDirect.cs b/osu.Desktop.Tests/Visual/TestCaseDirect.cs
index b78ea02767..9fd93c3f1e 100644
--- a/osu.Desktop.Tests/Visual/TestCaseDirect.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseDirect.cs
@@ -41,12 +41,14 @@ namespace osu.Desktop.Tests.Visual
{
new BeatmapSetInfo
{
+ OnlineBeatmapSetID = 578332,
Metadata = new BeatmapMetadata
{
Title = @"OrVid",
Artist = @"An",
Author = @"RLC",
Source = @"",
+ Tags = @"acuticnotes an-fillnote revid tear tearvid encrpted encryption axi axivid quad her hervid recoll",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
@@ -71,12 +73,14 @@ namespace osu.Desktop.Tests.Visual
},
new BeatmapSetInfo
{
+ OnlineBeatmapSetID = 599627,
Metadata = new BeatmapMetadata
{
Title = @"tiny lamp",
Artist = @"fhana",
Author = @"Sotarks",
Source = @"ぎんぎつね",
+ Tags = @"lantis junichi sato yuxuki waga kevin mitsunaga towana gingitsune opening op full ver version kalibe collab collaboration",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
@@ -101,12 +105,14 @@ namespace osu.Desktop.Tests.Visual
},
new BeatmapSetInfo
{
+ OnlineBeatmapSetID = 513268,
Metadata = new BeatmapMetadata
{
Title = @"At Gwanghwamun",
Artist = @"KYUHYUN",
Author = @"Cerulean Veyron",
Source = @"",
+ Tags = @"soul ballad kh super junior sj suju 슈퍼주니어 kt뮤직 sm엔터테인먼트 s.m.entertainment kt music 1st mini album ep",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
@@ -146,12 +152,14 @@ namespace osu.Desktop.Tests.Visual
},
new BeatmapSetInfo
{
+ OnlineBeatmapSetID = 586841,
Metadata = new BeatmapMetadata
{
Title = @"RHAPSODY OF BLUE SKY",
Artist = @"fhana",
Author = @"[Kamiya]",
Source = @"小林さんちのメイドラゴン",
+ Tags = @"kobayashi san chi no maidragon aozora no opening anime maid dragon oblivion karen dynamix imoutosan pata-mon gxytcgxytc",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
diff --git a/osu.Desktop.Tests/Visual/TestCaseEditorMenuBar.cs b/osu.Desktop.Tests/Visual/TestCaseEditorMenuBar.cs
new file mode 100644
index 0000000000..9cb3053ff2
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseEditorMenuBar.cs
@@ -0,0 +1,84 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Screens.Edit.Menus;
+
+namespace osu.Desktop.Tests.Visual
+{
+ public class TestCaseEditorMenuBar : OsuTestCase
+ {
+ public TestCaseEditorMenuBar()
+ {
+ Add(new EditorMenuBar
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Y = 50,
+ Items = new[]
+ {
+ new EditorMenuBarItem("File")
+ {
+ Items = new[]
+ {
+ new EditorMenuItem("Clear All Notes"),
+ new EditorMenuItem("Open Difficulty..."),
+ new EditorMenuItem("Save"),
+ new EditorMenuItem("Create a new Difficulty..."),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Revert to Saved"),
+ new EditorMenuItem("Revert to Saved (Full)"),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Test Beatmap"),
+ new EditorMenuItem("Open AiMod"),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Upload Beatmap..."),
+ new EditorMenuItem("Export Package"),
+ new EditorMenuItem("Export Map Package"),
+ new EditorMenuItem("Import from..."),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Open Song Folder"),
+ new EditorMenuItem("Open .osu in Notepad"),
+ new EditorMenuItem("Open .osb in Notepad"),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Exit"),
+ }
+ },
+ new EditorMenuBarItem("Timing")
+ {
+ Items = new[]
+ {
+ new EditorMenuItem("Time Signature"),
+ new EditorMenuItem("Metronome Clicks"),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Add Timing Section"),
+ new EditorMenuItem("Add Inheriting Section"),
+ new EditorMenuItem("Reset Current Section"),
+ new EditorMenuItem("Delete Timing Section"),
+ new EditorMenuItem("Resnap Current Section"),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Timing Setup"),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Resnap All Notes", MenuItemType.Destructive),
+ new EditorMenuItem("Move all notes in time...", MenuItemType.Destructive),
+ new EditorMenuItem("Recalculate Slider Lengths", MenuItemType.Destructive),
+ new EditorMenuItem("Delete All Timing Sections", MenuItemType.Destructive),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Set Current Position as Preview Point"),
+ }
+ },
+ new EditorMenuBarItem("Testing")
+ {
+ Items = new[]
+ {
+ new EditorMenuItem("Item 1"),
+ new EditorMenuItem("Item 2"),
+ new EditorMenuItem("Item 3"),
+ }
+ },
+ }
+ });
+ }
+ }
+}
diff --git a/osu.Desktop.Tests/Visual/TestCaseLeaderboard.cs b/osu.Desktop.Tests/Visual/TestCaseLeaderboard.cs
index 5f8ee8795c..f67db2f41a 100644
--- a/osu.Desktop.Tests/Visual/TestCaseLeaderboard.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseLeaderboard.cs
@@ -1,13 +1,13 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using OpenTK;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
-using OpenTK;
namespace osu.Desktop.Tests.Visual
{
@@ -24,7 +24,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.XH,
- Accuracy = 100,
+ Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -42,7 +42,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.X,
- Accuracy = 100,
+ Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -60,7 +60,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.SH,
- Accuracy = 100,
+ Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -78,7 +78,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.S,
- Accuracy = 100,
+ Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -96,7 +96,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.A,
- Accuracy = 100,
+ Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -114,7 +114,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.B,
- Accuracy = 98.26,
+ Accuracy = 0.9826,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -132,7 +132,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.C,
- Accuracy = 96.54,
+ Accuracy = 0.9654,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -150,7 +150,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.F,
- Accuracy = 60.25,
+ Accuracy = 0.6025,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -168,7 +168,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.F,
- Accuracy = 51.40,
+ Accuracy = 0.5140,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -186,7 +186,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.F,
- Accuracy = 42.22,
+ Accuracy = 0.4222,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
diff --git a/osu.Desktop.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Desktop.Tests/Visual/TestCasePlaySongSelect.cs
index 379100b543..8d1ae7d913 100644
--- a/osu.Desktop.Tests/Visual/TestCasePlaySongSelect.cs
+++ b/osu.Desktop.Tests/Visual/TestCasePlaySongSelect.cs
@@ -32,7 +32,7 @@ namespace osu.Desktop.Tests.Visual
backingDatabase.CreateTable();
rulesets = new RulesetStore(backingDatabase);
- manager = new BeatmapManager(storage, null, backingDatabase, rulesets);
+ manager = new BeatmapManager(storage, null, backingDatabase, rulesets, null);
for (int i = 0; i < 100; i += 10)
manager.Import(createTestBeatmapSet(i));
diff --git a/osu.Desktop.Tests/osu.Desktop.Tests.csproj b/osu.Desktop.Tests/osu.Desktop.Tests.csproj
index 24d112a45c..975af1a782 100644
--- a/osu.Desktop.Tests/osu.Desktop.Tests.csproj
+++ b/osu.Desktop.Tests/osu.Desktop.Tests.csproj
@@ -9,7 +9,7 @@
Properties
osu.Desktop.Tests
osu.Desktop.Tests
- v4.5
+ v4.6.1
512
@@ -87,6 +87,7 @@
+
diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj
index 8bba59207f..33019909c6 100644
--- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj
+++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj
@@ -22,7 +22,7 @@
OnOutputUpdated
false
LocalIntranet
- v4.5
+ v4.6.1
true
publish\
true
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index bbca4145c6..661c17699b 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -22,7 +22,7 @@
OnOutputUpdated
false
LocalIntranet
- v4.5
+ v4.6.1
true
publish\
true
diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs
index 96f8a8df9b..214cbc7f50 100644
--- a/osu.Game.Rulesets.Catch/CatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs
@@ -93,8 +93,6 @@ namespace osu.Game.Rulesets.Catch
}
}
- public override Mod GetAutoplayMod() => new ModAutoplay();
-
public override string Description => "osu!catch";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o };
diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
index 79ef5f4ba8..2ae2262ac7 100644
--- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
+++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
@@ -9,7 +9,7 @@
Properties
osu.Game.Rulesets.Catch
osu.Game.Rulesets.Catch
- v4.5
+ v4.6.1
512
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index ed46da4f85..d852a54ab6 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -105,8 +105,6 @@ namespace osu.Game.Rulesets.Mania
}
}
- public override Mod GetAutoplayMod() => new ModAutoplay();
-
public override string Description => "osu!mania";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs b/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs
index f44ad6fd60..bb11a05fc8 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs
@@ -68,6 +68,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModFadeIn : Mod
{
public override string Name => "FadeIn";
+ public override string ShortenedName => "FI";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
public override ModType Type => ModType.DifficultyIncrease;
public override double ScoreMultiplier => 1;
@@ -78,12 +79,14 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModRandom : Mod
{
public override string Name => "Random";
+ public override string ShortenedName => "RD";
public override string Description => @"Shuffle around the notes!";
public override double ScoreMultiplier => 1;
}
public abstract class ManiaKeyMod : Mod
{
+ public override string ShortenedName => Name;
public abstract int KeyCount { get; }
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
public override bool Ranked => true;
@@ -146,6 +149,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKeyCoop : Mod
{
public override string Name => "KeyCoop";
+ public override string ShortenedName => "2P";
public override string Description => @"Double the key amount, double the fun!";
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs
index a054e0db56..132c49ab31 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs
@@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModGravity : Mod, IGenerateSpeedAdjustments
{
public override string Name => "Gravity";
+ public override string ShortenedName => "GR";
public override double ScoreMultiplier => 0;
diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index ce4a84dc80..11f1d54b9d 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -197,7 +197,7 @@ namespace osu.Game.Rulesets.Mania.UI
{
if (action == Action)
{
- background.FadeTo(background.Alpha + 0.2f, 50, Easing.OutQuint);
+ background.FadeTo(0.6f, 50, Easing.OutQuint);
keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint);
}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
index 15f65b5545..8aa3a00f24 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
@@ -10,7 +10,6 @@ using osu.Framework.Graphics.Containers;
using System;
using osu.Game.Graphics;
using osu.Framework.Allocation;
-using OpenTK.Input;
using System.Linq;
using System.Collections.Generic;
using osu.Framework.Configuration;
@@ -24,12 +23,6 @@ namespace osu.Game.Rulesets.Mania.UI
{
public const float HIT_TARGET_POSITION = 50;
- ///
- /// Default column keys, expanding outwards from the middle as more column are added.
- /// E.g. 2 columns use FJ, 4 columns use DFJK, 6 use SDFJKL, etc...
- ///
- private static readonly Key[] default_keys = { Key.A, Key.S, Key.D, Key.F, Key.J, Key.K, Key.L, Key.Semicolon };
-
private SpecialColumnPosition specialColumnPosition;
///
/// The style to use for the special column.
diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
index 5c39139956..afd3b8e72d 100644
--- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
+++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
@@ -9,7 +9,7 @@
Properties
osu.Game.Rulesets.Mania
osu.Game.Rulesets.Mania
- v4.5
+ v4.6.1
512
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuMod.cs b/osu.Game.Rulesets.Osu/Mods/OsuMod.cs
index 432c6d391c..2970055bff 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuMod.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuMod.cs
@@ -96,6 +96,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModSpunOut : Mod
{
public override string Name => "Spun Out";
+ public override string ShortenedName => "SO";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout;
public override string Description => @"Spinners will be automatically completed";
public override double ScoreMultiplier => 0.9;
@@ -106,6 +107,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModAutopilot : Mod
{
public override string Name => "Autopilot";
+ public override string ShortenedName => "AP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_autopilot;
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
public override double ScoreMultiplier => 0;
@@ -126,6 +128,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModTarget : Mod
{
public override string Name => "Target";
+ public override string ShortenedName => "TP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_target;
public override string Description => @"";
public override double ScoreMultiplier => 1;
diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs
index 72ba421344..c6ecc3a506 100644
--- a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs
+++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs
@@ -51,6 +51,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
foreach (OsuDifficultyHitObject h in onScreen)
{
+ // ReSharper disable once PossibleNullReferenceException (resharper not smart enough to understand IEnumerator.MoveNext())
h.TimeUntilHit -= latest.DeltaTime;
// Calculate reading strain here
}
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index 00413331e0..c943518b0b 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -112,8 +112,6 @@ namespace osu.Game.Rulesets.Osu
}
}
- public override Mod GetAutoplayMod() => new OsuModAutoplay();
-
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new OsuDifficultyCalculator(beatmap);
diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
index 0c9e53cf69..857f47f9b9 100644
--- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
+++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
@@ -9,7 +9,7 @@
Properties
osu.Game.Rulesets.Osu
osu.Game.Rulesets.Osu
- v4.5
+ v4.6.1
512
diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
index 21d790169c..6b99b23a12 100644
--- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
+++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
@@ -242,6 +242,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
base.Reset();
Health.Value = 0;
+ Accuracy.Value = 1;
bonusScore = 0;
comboPortion = 0;
diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
index f457732085..7b1452766e 100644
--- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
@@ -95,8 +95,6 @@ namespace osu.Game.Rulesets.Taiko
}
}
- public override Mod GetAutoplayMod() => new TaikoModAutoplay();
-
public override string Description => "osu!taiko";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o };
diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
index 1e07907cc8..a840997214 100644
--- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
+++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
@@ -9,7 +9,7 @@
Properties
osu.Game.Rulesets.Taiko
osu.Game.Rulesets.Taiko
- v4.5
+ v4.6.1
512
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index 8ec68b41be..220b1aac7f 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -7,7 +7,7 @@
Library
osu.Game.Tests
osu.Game.Tests
- v4.5
+ v4.6.1
true
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index 82777734bb..15953fcd82 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -17,7 +17,7 @@ namespace osu.Game.Beatmaps
public class Beatmap
where T : HitObject
{
- public BeatmapInfo BeatmapInfo;
+ public BeatmapInfo BeatmapInfo = new BeatmapInfo();
public ControlPointInfo ControlPointInfo = new ControlPointInfo();
public List Breaks = new List();
public readonly List ComboColors = new List
@@ -33,7 +33,7 @@ namespace osu.Game.Beatmaps
///
/// The HitObjects this Beatmap contains.
///
- public List HitObjects;
+ public List HitObjects = new List();
///
/// Total amount of break time in the beatmap.
@@ -44,12 +44,13 @@ namespace osu.Game.Beatmaps
/// Constructs a new beatmap.
///
/// The original beatmap to use the parameters of.
- public Beatmap(Beatmap original = null)
+ public Beatmap(Beatmap original = null)
{
BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo;
ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo;
Breaks = original?.Breaks ?? Breaks;
ComboColors = original?.ComboColors ?? ComboColors;
+ HitObjects = original?.HitObjects ?? HitObjects;
}
}
@@ -65,7 +66,6 @@ namespace osu.Game.Beatmaps
public Beatmap(Beatmap original = null)
: base(original)
{
- HitObjects = original?.HitObjects;
}
}
}
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 551612330b..f58b3505c5 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -20,6 +20,9 @@ using osu.Game.IPC;
using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
using SQLite.Net;
+using osu.Game.Online.API.Requests;
+using System.Threading.Tasks;
+using osu.Game.Online.API;
namespace osu.Game.Beatmaps
{
@@ -63,6 +66,10 @@ namespace osu.Game.Beatmaps
private readonly BeatmapStore beatmaps;
+ private readonly APIAccess api;
+
+ private readonly List currentDownloads = new List();
+
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private BeatmapIPCChannel ipc;
@@ -76,7 +83,7 @@ namespace osu.Game.Beatmaps
///
public Func GetStableStorage { private get; set; }
- public BeatmapManager(Storage storage, FileStore files, SQLiteConnection connection, RulesetStore rulesets, IIpcHost importHost = null)
+ public BeatmapManager(Storage storage, FileStore files, SQLiteConnection connection, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null)
{
beatmaps = new BeatmapStore(connection);
beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s);
@@ -88,6 +95,7 @@ namespace osu.Game.Beatmaps
this.files = files;
this.connection = connection;
this.rulesets = rulesets;
+ this.api = api;
if (importHost != null)
ipc = new BeatmapIPCChannel(importHost, this);
@@ -177,6 +185,74 @@ namespace osu.Game.Beatmaps
beatmaps.Add(beatmapSetInfo);
}
+ ///
+ /// Downloads a beatmap.
+ ///
+ /// The to be downloaded.
+ /// A new , or an existing one if a download is already in progress.
+ public DownloadBeatmapSetRequest Download(BeatmapSetInfo beatmapSetInfo)
+ {
+ var existing = GetExistingDownload(beatmapSetInfo);
+
+ if (existing != null) return existing;
+
+ if (api == null) return null;
+
+ ProgressNotification downloadNotification = new ProgressNotification
+ {
+ Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
+ };
+
+ var request = new DownloadBeatmapSetRequest(beatmapSetInfo);
+
+ request.DownloadProgressed += progress =>
+ {
+ downloadNotification.State = ProgressNotificationState.Active;
+ downloadNotification.Progress = progress;
+ };
+
+ request.Success += data =>
+ {
+ downloadNotification.State = ProgressNotificationState.Completed;
+
+ using (var stream = new MemoryStream(data))
+ using (var archive = new OszArchiveReader(stream))
+ Import(archive);
+
+ currentDownloads.Remove(request);
+ };
+
+ request.Failure += data =>
+ {
+ downloadNotification.State = ProgressNotificationState.Completed;
+ Logger.Error(data, "Failed to get beatmap download information");
+ currentDownloads.Remove(request);
+ };
+
+ downloadNotification.CancelRequested += () =>
+ {
+ request.Cancel();
+ currentDownloads.Remove(request);
+ downloadNotification.State = ProgressNotificationState.Cancelled;
+ return true;
+ };
+
+ currentDownloads.Add(request);
+ PostNotification?.Invoke(downloadNotification);
+
+ // don't run in the main api queue as this is a long-running task.
+ Task.Run(() => request.Perform(api));
+
+ return request;
+ }
+
+ ///
+ /// Get an existing download request if it exists.
+ ///
+ /// The whose download request is wanted.
+ /// The object if it exists, or null.
+ public DownloadBeatmapSetRequest GetExistingDownload(BeatmapSetInfo beatmap) => currentDownloads.Find(d => d.BeatmapSet.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID);
+
///
/// Delete a beatmap from the manager.
/// Is a no-op for already deleted beatmaps.
diff --git a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs
index 4a389101e2..9c62289bfa 100644
--- a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs
+++ b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs
@@ -11,6 +11,8 @@ namespace osu.Game.Beatmaps.Drawables
{
public class BeatmapGroup : IStateful
{
+ public event Action StateChanged;
+
public BeatmapPanel SelectedPanel;
///
@@ -31,17 +33,18 @@ namespace osu.Game.Beatmaps.Drawables
public BeatmapSetHeader Header;
- private BeatmapGroupState state;
-
public List BeatmapPanels;
public BeatmapSetInfo BeatmapSet;
+ private BeatmapGroupState state;
public BeatmapGroupState State
{
get { return state; }
set
{
+ state = value;
+
switch (value)
{
case BeatmapGroupState.Expanded:
@@ -60,7 +63,8 @@ namespace osu.Game.Beatmaps.Drawables
panel.State = PanelSelectedState.Hidden;
break;
}
- state = value;
+
+ StateChanged?.Invoke(state);
}
}
@@ -90,6 +94,7 @@ namespace osu.Game.Beatmaps.Drawables
Header.AddDifficultyIcons(BeatmapPanels);
}
+
private void headerGainedSelection(BeatmapSetHeader panel)
{
State = BeatmapGroupState.Expanded;
diff --git a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs
index 2614baa116..41b77f6584 100644
--- a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs
+++ b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs
@@ -33,7 +33,8 @@ namespace osu.Game.Beatmaps.Drawables
Normal,
Hard,
Insane,
- Expert
+ Expert,
+ ExpertPlus
}
private DifficultyRating getDifficultyRating(BeatmapInfo beatmap)
@@ -44,7 +45,8 @@ namespace osu.Game.Beatmaps.Drawables
if (rating < 2.25) return DifficultyRating.Normal;
if (rating < 3.75) return DifficultyRating.Hard;
if (rating < 5.25) return DifficultyRating.Insane;
- return DifficultyRating.Expert;
+ if (rating < 6.75) return DifficultyRating.Expert;
+ return DifficultyRating.ExpertPlus;
}
private Color4 getColour(BeatmapInfo beatmap)
@@ -55,12 +57,14 @@ namespace osu.Game.Beatmaps.Drawables
return palette.Green;
default:
case DifficultyRating.Normal:
- return palette.Yellow;
+ return palette.Blue;
case DifficultyRating.Hard:
- return palette.Pink;
+ return palette.Yellow;
case DifficultyRating.Insane:
- return palette.Purple;
+ return palette.Pink;
case DifficultyRating.Expert:
+ return palette.Purple;
+ case DifficultyRating.ExpertPlus:
return palette.Gray0;
}
}
diff --git a/osu.Game/Beatmaps/Drawables/Panel.cs b/osu.Game/Beatmaps/Drawables/Panel.cs
index d07be88392..d6ed306b39 100644
--- a/osu.Game/Beatmaps/Drawables/Panel.cs
+++ b/osu.Game/Beatmaps/Drawables/Panel.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using osu.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -15,6 +16,8 @@ namespace osu.Game.Beatmaps.Drawables
{
public const float MAX_HEIGHT = 80;
+ public event Action StateChanged;
+
public override bool RemoveWhenNotAlive => false;
private readonly Container nestedContainer;
@@ -77,11 +80,15 @@ namespace osu.Game.Beatmaps.Drawables
set
{
- if (state == value) return;
+ if (state == value)
+ return;
var last = state;
state = value;
+
ApplyState(last);
+
+ StateChanged?.Invoke(State);
}
}
diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
index 479f274efb..ad0b110e48 100644
--- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
@@ -7,7 +7,6 @@ using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
@@ -42,10 +41,7 @@ namespace osu.Game.Beatmaps
this.game = game;
}
- protected override Beatmap GetBeatmap() => new Beatmap
- {
- HitObjects = new List(),
- };
+ protected override Beatmap GetBeatmap() => new Beatmap();
protected override Texture GetBackground() => game.Textures.Get(@"Backgrounds/bg4");
@@ -59,8 +55,6 @@ namespace osu.Game.Beatmaps
{
public override IEnumerable GetModsFor(ModType type) => new Mod[] { };
- public override Mod GetAutoplayMod() => new ModAutoplay();
-
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset)
{
throw new NotImplementedException();
diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs
index 234d65eee4..81695c3b5a 100644
--- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.IO;
-using osu.Game.Rulesets.Objects;
namespace osu.Game.Beatmaps.Formats
{
@@ -21,7 +20,7 @@ namespace osu.Game.Beatmaps.Formats
{
string line;
do { line = stream.ReadLine()?.Trim(); }
- while (line != null && line.Length == 0);
+ while (line != null && line.Length == 0);
if (line == null || !decoders.ContainsKey(line))
throw new IOException(@"Unknown file format");
@@ -47,7 +46,6 @@ namespace osu.Game.Beatmaps.Formats
{
var beatmap = new Beatmap
{
- HitObjects = new List(),
BeatmapInfo = new BeatmapInfo
{
Metadata = new BeatmapMetadata(),
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 462f94ed7c..4797f438d0 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -54,7 +54,7 @@ namespace osu.Game.Beatmaps
{
if (beatmap != null) return beatmap;
- beatmap = GetBeatmap();
+ beatmap = GetBeatmap() ?? new Beatmap();
// use the database-backed info.
beatmap.BeatmapInfo = BeatmapInfo;
diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
index 0713fa1a52..4ea4f4cdc3 100644
--- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
@@ -19,12 +19,12 @@ namespace osu.Game.Graphics.Containers
samplePopIn = audio.Sample.Get(@"UI/melodic-5");
samplePopOut = audio.Sample.Get(@"UI/melodic-4");
- StateChanged += OsuFocusedOverlayContainer_StateChanged;
+ StateChanged += onStateChanged;
}
- private void OsuFocusedOverlayContainer_StateChanged(VisibilityContainer arg1, Visibility arg2)
+ private void onStateChanged(Visibility visibility)
{
- switch (arg2)
+ switch (visibility)
{
case Visibility.Visible:
samplePopIn?.Play();
diff --git a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs
index b3e53280fb..65ece51a70 100644
--- a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs
+++ b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using OpenTK;
using osu.Framework;
using osu.Framework.Graphics;
@@ -35,6 +36,8 @@ namespace osu.Game.Graphics.UserInterface
private class BreadcrumbTabItem : OsuTabItem, IStateful
{
+ public event Action StateChanged;
+
public readonly SpriteIcon Chevron;
//don't allow clicking between transitions and don't make the chevron clickable
@@ -42,6 +45,7 @@ namespace osu.Game.Graphics.UserInterface
public override bool HandleInput => State == Visibility.Visible;
private Visibility state;
+
public Visibility State
{
get { return state; }
@@ -62,6 +66,8 @@ namespace osu.Game.Graphics.UserInterface
this.FadeOut(transition_duration, Easing.OutQuint);
this.ScaleTo(new Vector2(0.8f, 1f), transition_duration, Easing.OutQuint);
}
+
+ StateChanged?.Invoke(State);
}
}
diff --git a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs
index 808c72ee5d..4ce6c98744 100644
--- a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs
+++ b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs
@@ -14,14 +14,17 @@ namespace osu.Game.Graphics.UserInterface
private const int fade_duration = 250;
public OsuContextMenu()
+ : base(Direction.Vertical)
{
- CornerRadius = 5;
- EdgeEffect = new EdgeEffectParameters
+ MaskingContainer.CornerRadius = 5;
+ MaskingContainer.EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.1f),
Radius = 4,
};
+
+ ItemsContainer.Padding = new MarginPadding { Vertical = DrawableOsuMenuItem.MARGIN_VERTICAL };
}
[BackgroundDependencyLoader]
@@ -32,7 +35,5 @@ namespace osu.Game.Graphics.UserInterface
protected override void AnimateOpen() => this.FadeIn(fade_duration, Easing.OutQuint);
protected override void AnimateClose() => this.FadeOut(fade_duration, Easing.OutQuint);
-
- protected override MarginPadding ItemFlowContainerPadding => new MarginPadding { Vertical = DrawableOsuMenuItem.MARGIN_VERTICAL };
}
}
\ No newline at end of file
diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
index dde154bb61..f605804aaa 100644
--- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs
+++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
@@ -57,6 +57,9 @@ namespace osu.Game.Graphics.UserInterface
{
CornerRadius = 4;
BackgroundColour = Color4.Black.Opacity(0.5f);
+
+ // todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
+ ItemsContainer.Padding = new MarginPadding(5);
}
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
@@ -64,13 +67,18 @@ namespace osu.Game.Graphics.UserInterface
protected override void AnimateClose() => this.FadeOut(300, Easing.OutQuint);
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
- protected override MarginPadding ItemFlowContainerPadding => new MarginPadding(5);
-
- // todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
- protected override void UpdateMenuHeight()
+ protected override void UpdateSize(Vector2 newSize)
{
- var actualHeight = (RelativeSizeAxes & Axes.Y) > 0 ? 1 : ContentHeight;
- this.ResizeHeightTo(State == MenuState.Opened ? actualHeight : 0, 300, Easing.OutQuint);
+ if (Direction == Direction.Vertical)
+ {
+ Width = newSize.X;
+ this.ResizeHeightTo(newSize.Y, 300, Easing.OutQuint);
+ }
+ else
+ {
+ Height = newSize.Y;
+ this.ResizeWidthTo(newSize.X, 300, Easing.OutQuint);
+ }
}
private Color4 accentColour;
@@ -88,7 +96,7 @@ namespace osu.Game.Graphics.UserInterface
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuDropdownMenuItem(item) { AccentColour = accentColour };
#region DrawableOsuDropdownMenuItem
- protected class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour
+ public class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour
{
private Color4? accentColour;
public Color4 AccentColour
@@ -141,7 +149,7 @@ namespace osu.Game.Graphics.UserInterface
protected override Drawable CreateContent() => new Content();
- protected class Content : FillFlowContainer, IHasText
+ protected new class Content : FillFlowContainer, IHasText
{
public string Text
{
diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs
index ab37fd2c90..3fd5481152 100644
--- a/osu.Game/Graphics/UserInterface/OsuMenu.cs
+++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs
@@ -12,30 +12,45 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
+using OpenTK;
namespace osu.Game.Graphics.UserInterface
{
public class OsuMenu : Menu
{
- public OsuMenu()
+ public OsuMenu(Direction direction, bool topLevelMenu = false)
+ : base(direction, topLevelMenu)
{
- CornerRadius = 4;
BackgroundColour = Color4.Black.Opacity(0.5f);
+
+ MaskingContainer.CornerRadius = 4;
+ ItemsContainer.Padding = new MarginPadding(5);
}
protected override void AnimateOpen() => this.FadeIn(300, Easing.OutQuint);
protected override void AnimateClose() => this.FadeOut(300, Easing.OutQuint);
- protected override void UpdateMenuHeight()
+ protected override void UpdateSize(Vector2 newSize)
{
- var actualHeight = (RelativeSizeAxes & Axes.Y) > 0 ? 1 : ContentHeight;
- this.ResizeHeightTo(State == MenuState.Opened ? actualHeight : 0, 300, Easing.OutQuint);
+ if (Direction == Direction.Vertical)
+ {
+ Width = newSize.X;
+ this.ResizeHeightTo(newSize.Y, 300, Easing.OutQuint);
+ }
+ else
+ {
+ Height = newSize.Y;
+ this.ResizeWidthTo(newSize.X, 300, Easing.OutQuint);
+ }
}
- protected override MarginPadding ItemFlowContainerPadding => new MarginPadding(5);
-
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuMenuItem(item);
+ protected override Menu CreateSubMenu() => new OsuMenu(Direction.Vertical)
+ {
+ Anchor = Direction == Direction.Horizontal ? Anchor.BottomLeft : Anchor.TopRight
+ };
+
protected class DrawableOsuMenuItem : DrawableMenuItem
{
private const int margin_horizontal = 17;
@@ -51,7 +66,6 @@ namespace osu.Game.Graphics.UserInterface
public DrawableOsuMenuItem(MenuItem item)
: base(item)
{
-
}
[BackgroundDependencyLoader]
@@ -104,9 +118,10 @@ namespace osu.Game.Graphics.UserInterface
return base.OnClick(state);
}
- protected override Drawable CreateContent() => text = new TextContainer();
+ protected sealed override Drawable CreateContent() => text = CreateTextContainer();
+ protected virtual TextContainer CreateTextContainer() => new TextContainer();
- private class TextContainer : Container, IHasText
+ protected class TextContainer : Container, IHasText
{
public string Text
{
diff --git a/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs b/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs
index 4c108e793a..8c777f491b 100644
--- a/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs
+++ b/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs
@@ -15,11 +15,7 @@ namespace osu.Game.Graphics.UserInterface.Volume
{
private readonly VolumeMeter volumeMeterMaster;
- private void volumeChanged(double newVolume)
- {
- Show();
- schedulePopOut();
- }
+ protected override bool BlockPassThroughMouse => false;
public VolumeControl()
{
@@ -85,6 +81,12 @@ namespace osu.Game.Graphics.UserInterface.Volume
return false;
}
+ private void volumeChanged(double newVolume)
+ {
+ Show();
+ schedulePopOut();
+ }
+
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
diff --git a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs
new file mode 100644
index 0000000000..5a9f609bca
--- /dev/null
+++ b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs
@@ -0,0 +1,24 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Beatmaps;
+using System;
+
+namespace osu.Game.Online.API.Requests
+{
+ public class DownloadBeatmapSetRequest : APIDownloadRequest
+ {
+ public readonly BeatmapSetInfo BeatmapSet;
+
+ public Action DownloadProgressed;
+
+ public DownloadBeatmapSetRequest(BeatmapSetInfo set)
+ {
+ BeatmapSet = set;
+
+ Progress += (current, total) => DownloadProgressed?.Invoke((float) current / total);
+ }
+
+ protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download";
+ }
+}
diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs
index 966049429e..ef9ee85d25 100644
--- a/osu.Game/Online/API/Requests/GetScoresRequest.cs
+++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs
@@ -1,10 +1,14 @@
// Copyright (c) 2007-2017 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 Newtonsoft.Json;
using osu.Framework.IO.Network;
using osu.Game.Beatmaps;
+using osu.Game.Users;
+using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Online.API.Requests
@@ -16,6 +20,14 @@ namespace osu.Game.Online.API.Requests
public GetScoresRequest(BeatmapInfo beatmap)
{
this.beatmap = beatmap;
+
+ Success += onSuccess;
+ }
+
+ private void onSuccess(GetScoresResponse r)
+ {
+ foreach (OnlineScore score in r.Scores)
+ score.ApplyBeatmap(beatmap);
}
protected override WebRequest CreateWebRequest()
@@ -32,6 +44,88 @@ namespace osu.Game.Online.API.Requests
public class GetScoresResponse
{
[JsonProperty(@"scores")]
- public IEnumerable Scores;
+ public IEnumerable Scores;
}
-}
\ No newline at end of file
+
+ public class OnlineScore : Score
+ {
+ [JsonProperty(@"score")]
+ private double totalScore
+ {
+ set { TotalScore = value; }
+ }
+
+ [JsonProperty(@"max_combo")]
+ private int maxCombo
+ {
+ set { MaxCombo = value; }
+ }
+
+ [JsonProperty(@"user")]
+ private User user
+ {
+ set { User = value; }
+ }
+
+ [JsonProperty(@"replay_data")]
+ private Replay replay
+ {
+ set { Replay = value; }
+ }
+
+ [JsonProperty(@"score_id")]
+ private long onlineScoreID
+ {
+ set { OnlineScoreID = value; }
+ }
+
+ [JsonProperty(@"created_at")]
+ private DateTimeOffset date
+ {
+ set { Date = value; }
+ }
+
+ [JsonProperty(@"statistics")]
+ private Dictionary jsonStats
+ {
+ set
+ {
+ foreach (var kvp in value)
+ {
+ string key = kvp.Key;
+ switch (key)
+ {
+ case @"count_300":
+ key = @"300";
+ break;
+ case @"count_100":
+ key = @"100";
+ break;
+ case @"count_50":
+ key = @"50";
+ break;
+ case @"count_miss":
+ key = @"x";
+ break;
+ default:
+ continue;
+ }
+
+ Statistics.Add(key, kvp.Value);
+ }
+ }
+ }
+
+ [JsonProperty(@"mods")]
+ private string[] modStrings { get; set; }
+
+ public void ApplyBeatmap(BeatmapInfo beatmap)
+ {
+ Beatmap = beatmap;
+ Ruleset = beatmap.Ruleset;
+
+ // Evaluate the mod string
+ Mods = Ruleset.CreateInstance().GetAllMods().Where(mod => modStrings.Contains(mod.ShortenedName)).ToArray();
+ }
+ }
+}
diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs
index bec4aadb18..77683ae857 100644
--- a/osu.Game/Online/Chat/Channel.cs
+++ b/osu.Game/Online/Chat/Channel.cs
@@ -30,7 +30,7 @@ namespace osu.Game.Online.Chat
public Bindable Joined = new Bindable();
- public bool ReadOnly => Name != "#lazer";
+ public bool ReadOnly => false;
public const int MAX_HISTORY = 300;
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 30bc09d50f..b4fbdfb252 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -230,13 +230,13 @@ namespace osu.Game
var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct };
foreach (var overlay in singleDisplayOverlays)
{
- overlay.StateChanged += (container, state) =>
+ overlay.StateChanged += state =>
{
if (state == Visibility.Hidden) return;
foreach (var c in singleDisplayOverlays)
{
- if (c == container) continue;
+ if (c == overlay) continue;
c.State = Visibility.Hidden;
}
};
@@ -245,7 +245,11 @@ namespace osu.Game
LoadComponentAsync(Toolbar = new Toolbar
{
Depth = -4,
- OnHome = delegate { intro?.ChildScreen?.MakeCurrent(); },
+ OnHome = delegate
+ {
+ hideAllOverlays();
+ intro?.ChildScreen?.MakeCurrent();
+ },
}, overlayContent.Add);
settings.StateChanged += delegate
@@ -310,6 +314,16 @@ namespace osu.Game
private OsuScreen currentScreen;
private FrameworkConfigManager frameworkConfig;
+ private void hideAllOverlays()
+ {
+ settings.State = Visibility.Hidden;
+ chat.State = Visibility.Hidden;
+ direct.State = Visibility.Hidden;
+ social.State = Visibility.Hidden;
+ userProfile.State = Visibility.Hidden;
+ notificationOverlay.State = Visibility.Hidden;
+ }
+
private void screenChanged(Screen newScreen)
{
currentScreen = newScreen as OsuScreen;
@@ -323,19 +337,12 @@ namespace osu.Game
//central game screen change logic.
if (!currentScreen.ShowOverlays)
{
- settings.State = Visibility.Hidden;
- Toolbar.State = Visibility.Hidden;
+ hideAllOverlays();
musicController.State = Visibility.Hidden;
- chat.State = Visibility.Hidden;
- direct.State = Visibility.Hidden;
- social.State = Visibility.Hidden;
- userProfile.State = Visibility.Hidden;
- notificationOverlay.State = Visibility.Hidden;
+ Toolbar.State = Visibility.Hidden;
}
else
- {
Toolbar.State = Visibility.Visible;
- }
ScreenChanged?.Invoke(newScreen);
}
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index a7136ce803..76eb7d5101 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -106,9 +106,15 @@ namespace osu.Game
connection.CreateTable();
+ dependencies.Cache(API = new APIAccess
+ {
+ Username = LocalConfig.Get(OsuSetting.Username),
+ Token = LocalConfig.Get(OsuSetting.Token)
+ });
+
dependencies.Cache(RulesetStore = new RulesetStore(connection));
dependencies.Cache(FileStore = new FileStore(connection, Host.Storage));
- dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, connection, RulesetStore, Host));
+ dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, connection, RulesetStore, API, Host));
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, connection, Host, BeatmapManager, RulesetStore));
dependencies.Cache(KeyBindingStore = new KeyBindingStore(connection, RulesetStore));
dependencies.Cache(new OsuColour());
@@ -144,12 +150,6 @@ namespace osu.Game
Beatmap = new NonNullableBindable(defaultBeatmap);
BeatmapManager.DefaultBeatmap = defaultBeatmap;
- dependencies.Cache(API = new APIAccess
- {
- Username = LocalConfig.Get(OsuSetting.Username),
- Token = LocalConfig.Get(OsuSetting.Token)
- });
-
Beatmap.ValueChanged += b =>
{
// compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)
diff --git a/osu.Game/Overlays/Chat/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelListItem.cs
index f43154ea20..8360e793d8 100644
--- a/osu.Game/Overlays/Chat/ChannelListItem.cs
+++ b/osu.Game/Overlays/Chat/ChannelListItem.cs
@@ -76,7 +76,6 @@ namespace osu.Game.Overlays.Chat
Size = new Vector2(text_size),
Shadow = false,
Margin = new MarginPadding { Right = 10f },
- Alpha = 0f,
},
},
},
@@ -109,7 +108,6 @@ namespace osu.Game.Overlays.Chat
TextSize = text_size,
Font = @"Exo2.0-SemiBold",
Shadow = false,
- Alpha = 0.8f,
},
},
},
@@ -151,6 +149,9 @@ namespace osu.Game.Overlays.Chat
joinedBind.ValueChanged += updateColour;
joinedBind.BindTo(channel.Joined);
+
+ joinedBind.TriggerChange();
+ FinishTransforms(true);
}
protected override bool OnHover(InputState state)
diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs
index 4ff9169877..9f1028c168 100644
--- a/osu.Game/Overlays/Chat/ChatTabControl.cs
+++ b/osu.Game/Overlays/Chat/ChatTabControl.cs
@@ -16,15 +16,16 @@ using osu.Game.Online.Chat;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Configuration;
+using System;
namespace osu.Game.Overlays.Chat
{
public class ChatTabControl : OsuTabControl
{
- protected override TabItem CreateTabItem(Channel value) => new ChannelTabItem(value);
-
private const float shear_width = 10;
+ public Action OnRequestLeave;
+
public readonly Bindable ChannelSelectorActive = new Bindable();
private readonly ChannelTabItem.ChannelSelectorTabItem selectorTab;
@@ -49,6 +50,20 @@ namespace osu.Game.Overlays.Chat
ChannelSelectorActive.BindTo(selectorTab.Active);
}
+ protected override void AddTabItem(TabItem item, bool addToDropdown = true)
+ {
+ if (selectorTab.Depth < float.MaxValue)
+ // performTabSort might've made selectorTab's position wonky, fix it
+ TabContainer.ChangeChildDepth(selectorTab, float.MaxValue);
+
+ base.AddTabItem(item, addToDropdown);
+
+ if (SelectedTab == null)
+ SelectTab(item);
+ }
+
+ protected override TabItem CreateTabItem(Channel value) => new ChannelTabItem(value) { OnRequestClose = tabCloseRequested };
+
protected override void SelectTab(TabItem tab)
{
if (tab is ChannelTabItem.ChannelSelectorTabItem)
@@ -62,18 +77,38 @@ namespace osu.Game.Overlays.Chat
base.SelectTab(tab);
}
+ private void tabCloseRequested(TabItem tab)
+ {
+ int totalTabs = TabContainer.Count - 1; // account for selectorTab
+ int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(tab), 1, totalTabs);
+
+ if (tab == SelectedTab && totalTabs > 1)
+ // Select the tab after tab-to-be-removed's index, or the tab before if current == last
+ SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]);
+ else if (totalTabs == 1 && !selectorTab.Active)
+ // Open channel selection overlay if all channel tabs will be closed after removing this tab
+ SelectTab(selectorTab);
+
+ OnRequestLeave?.Invoke(tab.Value);
+ }
+
private class ChannelTabItem : TabItem
{
private Color4 backgroundInactive;
private Color4 backgroundHover;
private Color4 backgroundActive;
+ public override bool IsRemovable => !Pinned;
+
private readonly SpriteText text;
private readonly SpriteText textBold;
+ private readonly ClickableContainer closeButton;
private readonly Box box;
private readonly Box highlightBox;
private readonly SpriteIcon icon;
+ public Action OnRequestClose;
+
private void updateState()
{
if (Active)
@@ -108,6 +143,9 @@ namespace osu.Game.Overlays.Chat
protected override bool OnHover(InputState state)
{
+ if (IsRemovable)
+ closeButton.FadeIn(200, Easing.OutQuint);
+
if (!Active)
box.FadeColour(backgroundHover, transition_length, Easing.OutQuint);
return true;
@@ -115,6 +153,7 @@ namespace osu.Game.Overlays.Chat
protected override void OnHoverLost(InputState state)
{
+ closeButton.FadeOut(200, Easing.OutQuint);
updateState();
}
@@ -204,13 +243,69 @@ namespace osu.Game.Overlays.Chat
Font = @"Exo2.0-Bold",
TextSize = 18,
},
- }
- }
+ closeButton = new CloseButton
+ {
+ Alpha = 0,
+ Margin = new MarginPadding { Right = 20 },
+ Origin = Anchor.CentreRight,
+ Anchor = Anchor.CentreRight,
+ Action = delegate
+ {
+ if (IsRemovable) OnRequestClose?.Invoke(this);
+ },
+ },
+ },
+ },
};
}
+ public class CloseButton : ClickableContainer
+ {
+ private readonly SpriteIcon icon;
+
+ public CloseButton()
+ {
+ Size = new Vector2(20);
+
+ Child = icon = new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Scale = new Vector2(0.75f),
+ Icon = FontAwesome.fa_close,
+ RelativeSizeAxes = Axes.Both,
+ };
+ }
+
+ protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ {
+ icon.ScaleTo(0.5f, 1000, Easing.OutQuint);
+ return base.OnMouseDown(state, args);
+ }
+
+ protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
+ {
+ icon.ScaleTo(0.75f, 1000, Easing.OutElastic);
+ return base.OnMouseUp(state, args);
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ icon.FadeColour(Color4.Red, 200, Easing.OutQuint);
+ return base.OnHover(state);
+ }
+
+ protected override void OnHoverLost(InputState state)
+ {
+ icon.FadeColour(Color4.White, 200, Easing.OutQuint);
+ base.OnHoverLost(state);
+ }
+ }
+
public class ChannelSelectorTabItem : ChannelTabItem
{
+ public override bool IsRemovable => false;
+
public ChannelSelectorTabItem(Channel value) : base(value)
{
Depth = float.MaxValue;
diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs
index 6dd5425fd1..b1deae8272 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -160,6 +160,7 @@ namespace osu.Game.Overlays
channelTabs = new ChatTabControl
{
RelativeSizeAxes = Axes.Both,
+ OnRequestLeave = removeChannel,
},
}
},
@@ -169,7 +170,7 @@ namespace osu.Game.Overlays
channelTabs.Current.ValueChanged += newChannel => CurrentChannel = newChannel;
channelTabs.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden;
- channelSelection.StateChanged += (overlay, state) =>
+ channelSelection.StateChanged += state =>
{
channelTabs.ChannelSelectorActive.Value = state == Visibility.Visible;
@@ -305,6 +306,7 @@ namespace osu.Game.Overlays
addChannel(channels.Find(c => c.Name == @"#lobby"));
channelSelection.OnRequestJoin = addChannel;
+ channelSelection.OnRequestLeave = removeChannel;
channelSelection.Sections = new[]
{
new ChannelSection
@@ -332,7 +334,15 @@ namespace osu.Game.Overlays
set
{
- if (currentChannel == value || value == null) return;
+ if (currentChannel == value) return;
+
+ if (value == null)
+ {
+ currentChannel = null;
+ textbox.Current.Disabled = true;
+ currentChannelContainer.Clear(false);
+ return;
+ }
currentChannel = value;
@@ -391,6 +401,19 @@ namespace osu.Game.Overlays
channel.Joined.Value = true;
}
+ private void removeChannel(Channel channel)
+ {
+ if (channel == null) return;
+
+ if (channel == CurrentChannel) CurrentChannel = null;
+
+ careChannels.Remove(channel);
+ loadedChannels.Remove(loadedChannels.Find(c => c.Channel == channel));
+ channelTabs.RemoveItem(channel);
+
+ channel.Joined.Value = false;
+ }
+
private void fetchInitialMessages(Channel channel)
{
var req = new GetMessagesRequest(new List { channel }, null);
diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs
index 012e93f10d..7853eefd2c 100644
--- a/osu.Game/Overlays/DialogOverlay.cs
+++ b/osu.Game/Overlays/DialogOverlay.cs
@@ -26,7 +26,7 @@ namespace osu.Game.Overlays
dialogContainer.Add(currentDialog);
currentDialog.Show();
- currentDialog.StateChanged += onDialogOnStateChanged;
+ currentDialog.StateChanged += state => onDialogOnStateChanged(dialog, state);
State = Visibility.Visible;
}
diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs
index a642f72821..6f1f581d0b 100644
--- a/osu.Game/Overlays/Direct/DirectPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectPanel.cs
@@ -4,8 +4,6 @@
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
-using System.IO;
-using System.Threading.Tasks;
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -20,8 +18,8 @@ using osu.Framework.Input;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Framework.Logging;
-using osu.Game.Beatmaps.IO;
using osu.Game.Overlays.Notifications;
+using osu.Game.Online.API.Requests;
namespace osu.Game.Overlays.Direct
{
@@ -97,6 +95,11 @@ namespace osu.Game.Overlays.Direct
},
}
});
+
+ var downloadRequest = beatmaps.GetExistingDownload(SetInfo);
+
+ if (downloadRequest != null)
+ attachDownload(downloadRequest);
}
protected override bool OnHover(InputState state)
@@ -115,23 +118,8 @@ namespace osu.Game.Overlays.Direct
base.OnHoverLost(state);
}
- // this should eventually be moved to a more central place, like BeatmapManager.
- private DownloadBeatmapSetRequest downloadRequest;
-
protected void StartDownload()
{
- if (api == null) return;
-
- // we already have an active download running.
- if (downloadRequest != null)
- {
- content.MoveToX(-5, 50, Easing.OutSine).Then()
- .MoveToX(5, 100, Easing.InOutSine).Then()
- .MoveToX(-5, 100, Easing.InOutSine).Then()
- .MoveToX(0, 50, Easing.InSine).Then();
- return;
- }
-
if (!api.LocalUser.Value.IsSupporter)
{
notifications.Post(new SimpleNotification
@@ -142,72 +130,43 @@ namespace osu.Game.Overlays.Direct
return;
}
+ if (beatmaps.GetExistingDownload(SetInfo) != null)
+ {
+ // we already have an active download running.
+ content.MoveToX(-5, 50, Easing.OutSine).Then()
+ .MoveToX(5, 100, Easing.InOutSine).Then()
+ .MoveToX(-5, 100, Easing.InOutSine).Then()
+ .MoveToX(0, 50, Easing.InSine).Then();
+
+ return;
+ }
+
+ var request = beatmaps.Download(SetInfo);
+
+ attachDownload(request);
+ }
+
+ private void attachDownload(DownloadBeatmapSetRequest request)
+ {
progressBar.FadeIn(400, Easing.OutQuint);
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
progressBar.Current.Value = 0;
- ProgressNotification downloadNotification = new ProgressNotification
- {
- Text = $"Downloading {SetInfo.Metadata.Artist} - {SetInfo.Metadata.Title}",
- };
-
- downloadRequest = new DownloadBeatmapSetRequest(SetInfo);
- downloadRequest.Failure += e =>
+ request.Failure += e =>
{
progressBar.Current.Value = 0;
progressBar.FadeOut(500);
- downloadNotification.State = ProgressNotificationState.Completed;
Logger.Error(e, "Failed to get beatmap download information");
-
- downloadRequest = null;
};
- downloadRequest.Progress += (current, total) =>
- {
- float progress = (float)current / total;
+ request.DownloadProgressed += progress => progressBar.Current.Value = progress;
- progressBar.Current.Value = progress;
-
- downloadNotification.State = ProgressNotificationState.Active;
- downloadNotification.Progress = progress;
- };
-
- downloadRequest.Success += data =>
+ request.Success += data =>
{
progressBar.Current.Value = 1;
progressBar.FadeOut(500);
-
- downloadNotification.State = ProgressNotificationState.Completed;
-
- using (var stream = new MemoryStream(data))
- using (var archive = new OszArchiveReader(stream))
- beatmaps.Import(archive);
};
-
- downloadNotification.CancelRequested += () =>
- {
- downloadRequest.Cancel();
- downloadRequest = null;
- return true;
- };
-
- notifications.Post(downloadNotification);
-
- // don't run in the main api queue as this is a long-running task.
- Task.Run(() => downloadRequest.Perform(api));
- }
-
- public class DownloadBeatmapSetRequest : APIDownloadRequest
- {
- private readonly BeatmapSetInfo beatmapSet;
-
- public DownloadBeatmapSetRequest(BeatmapSetInfo beatmapSet)
- {
- this.beatmapSet = beatmapSet;
- }
-
- protected override string Target => $@"beatmapsets/{beatmapSet.OnlineBeatmapSetID}/download";
}
protected override void LoadComplete()
diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs
index f734e43826..9c07e1087f 100644
--- a/osu.Game/Overlays/DirectOverlay.cs
+++ b/osu.Game/Overlays/DirectOverlay.cs
@@ -27,6 +27,7 @@ namespace osu.Game.Overlays
private APIAccess api;
private RulesetStore rulesets;
+ private BeatmapManager beatmaps;
private readonly FillFlowContainer resultCountsContainer;
private readonly OsuSpriteText resultCountsText;
@@ -46,9 +47,26 @@ namespace osu.Game.Overlays
set
{
if (beatmapSets?.Equals(value) ?? false) return;
+
beatmapSets = value;
- recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
+ if (beatmapSets == null) return;
+
+ var artists = new List();
+ var songs = new List();
+ var tags = new List();
+ foreach (var s in beatmapSets)
+ {
+ artists.Add(s.Metadata.Artist);
+ songs.Add(s.Metadata.Title);
+ tags.AddRange(s.Metadata.Tags.Split(' '));
+ }
+
+ ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
+
+ if (beatmapSets.Any() && panels == null)
+ // real use case? currently only seems to be for test case
+ recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
}
}
@@ -147,6 +165,8 @@ namespace osu.Game.Overlays
{
this.api = api;
this.rulesets = rulesets;
+ this.beatmaps = beatmaps;
+
resultCountsContainer.Colour = colours.Yellow;
beatmaps.BeatmapSetAdded += setAdded;
@@ -156,6 +176,7 @@ namespace osu.Game.Overlays
{
// if a new map was imported, we should remove it from search results (download completed etc.)
panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire();
+ BeatmapSets = BeatmapSets?.Where(b => b.OnlineBeatmapSetID != set.OnlineBeatmapSetID);
}
private void updateResultCounts()
@@ -237,20 +258,11 @@ namespace osu.Game.Overlays
getSetsRequest.Success += r =>
{
- BeatmapSets = r?.Select(response => response.ToBeatmapSet(rulesets));
- if (BeatmapSets == null) return;
+ BeatmapSets = r?.
+ Select(response => response.ToBeatmapSet(rulesets)).
+ Where(b => beatmaps.QueryBeatmapSet(q => q.OnlineBeatmapSetID == b.OnlineBeatmapSetID) == null);
- var artists = new List();
- var songs = new List();
- var tags = new List();
- foreach (var s in BeatmapSets)
- {
- artists.Add(s.Metadata.Artist);
- songs.Add(s.Metadata.Title);
- tags.AddRange(s.Metadata.Tags.Split(' '));
- }
-
- ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
+ recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
};
api.Queue(getSetsRequest);
diff --git a/osu.Game/Overlays/MainSettings.cs b/osu.Game/Overlays/MainSettings.cs
index b4d9cac045..4fe86d62fd 100644
--- a/osu.Game/Overlays/MainSettings.cs
+++ b/osu.Game/Overlays/MainSettings.cs
@@ -53,7 +53,7 @@ namespace osu.Game.Overlays
private const float hidden_width = 120;
- private void keyBindingOverlay_StateChanged(VisibilityContainer container, Visibility visibility)
+ private void keyBindingOverlay_StateChanged(Visibility visibility)
{
switch (visibility)
{
diff --git a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs
index 56b26e7176..3ac8af7b2b 100644
--- a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs
+++ b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using osu.Framework;
using OpenTK;
using osu.Framework.Allocation;
@@ -19,6 +20,8 @@ namespace osu.Game.Overlays.MedalSplash
private const float scale_when_unlocked = 0.76f;
private const float scale_when_full = 0.6f;
+ public event Action StateChanged;
+
private readonly Medal medal;
private readonly Container medalContainer;
private readonly Sprite medalSprite, medalGlow;
@@ -132,6 +135,8 @@ namespace osu.Game.Overlays.MedalSplash
state = value;
updateState();
+
+ StateChanged?.Invoke(State);
}
}
diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs
index cb4628825e..64d0d628f0 100644
--- a/osu.Game/Overlays/MusicController.cs
+++ b/osu.Game/Overlays/MusicController.cs
@@ -204,7 +204,7 @@ namespace osu.Game.Overlays
beatmapBacking.BindTo(game.Beatmap);
- playlist.StateChanged += (c, s) => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
+ playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
}
protected override void LoadComplete()
@@ -302,8 +302,8 @@ namespace osu.Game.Overlays
else
{
//figure out the best direction based on order in playlist.
- var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo.ID).Count();
- var next = beatmap == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmap.BeatmapSetInfo.ID).Count();
+ var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count();
+ var next = beatmap == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmap.BeatmapSetInfo?.ID).Count();
direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
}
diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs
index a816fa56c1..e62050fae1 100644
--- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs
@@ -292,6 +292,8 @@ namespace osu.Game.Overlays.Settings.Sections.General
Colour = Color4.Black.Opacity(0.25f),
Radius = 4,
};
+
+ ItemsContainer.Padding = new MarginPadding();
}
[BackgroundDependencyLoader]
@@ -300,8 +302,6 @@ namespace osu.Game.Overlays.Settings.Sections.General
BackgroundColour = colours.Gray3;
}
- protected override MarginPadding ItemFlowContainerPadding => new MarginPadding();
-
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableUserDropdownMenuItem(item);
private class DrawableUserDropdownMenuItem : DrawableOsuDropdownMenuItem
diff --git a/osu.Game/Overlays/Settings/Sidebar.cs b/osu.Game/Overlays/Settings/Sidebar.cs
index b22a5ab126..55167188a3 100644
--- a/osu.Game/Overlays/Settings/Sidebar.cs
+++ b/osu.Game/Overlays/Settings/Sidebar.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using System.Linq;
using osu.Framework;
using OpenTK;
@@ -19,6 +20,9 @@ namespace osu.Game.Overlays.Settings
private readonly FillFlowContainer content;
internal const float DEFAULT_WIDTH = ToolbarButton.WIDTH;
internal const int EXPANDED_WIDTH = 200;
+
+ public event Action StateChanged;
+
protected override Container Content => content;
public Sidebar()
@@ -102,6 +106,8 @@ namespace osu.Game.Overlays.Settings
this.ResizeTo(new Vector2(EXPANDED_WIDTH, Height), 500, Easing.OutQuint);
break;
}
+
+ StateChanged?.Invoke(State);
}
}
diff --git a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs
index 6d61a096b2..c530e8d7ff 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs
@@ -45,7 +45,7 @@ namespace osu.Game.Overlays.Toolbar
stateContainer.StateChanged -= stateChanged;
}
- private void stateChanged(VisibilityContainer c, Visibility state)
+ private void stateChanged(Visibility state)
{
switch (state)
{
diff --git a/osu.Game/Overlays/WaveOverlayContainer.cs b/osu.Game/Overlays/WaveOverlayContainer.cs
index fd89dcfbc4..4f9783a762 100644
--- a/osu.Game/Overlays/WaveOverlayContainer.cs
+++ b/osu.Game/Overlays/WaveOverlayContainer.cs
@@ -167,6 +167,8 @@ namespace osu.Game.Overlays
private class Wave : Container, IStateful
{
+ public event Action StateChanged;
+
public float FinalPosition;
public Wave()
@@ -200,6 +202,7 @@ namespace osu.Game.Overlays
}
private Visibility state;
+
public Visibility State
{
get { return state; }
@@ -216,6 +219,8 @@ namespace osu.Game.Overlays
this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show);
break;
}
+
+ StateChanged?.Invoke(State);
}
}
}
diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs
index f54267af29..7b0034863e 100644
--- a/osu.Game/Rulesets/Mods/Mod.cs
+++ b/osu.Game/Rulesets/Mods/Mod.cs
@@ -16,6 +16,11 @@ namespace osu.Game.Rulesets.Mods
///
public abstract string Name { get; }
+ ///
+ /// The shortened name of this mod.
+ ///
+ public abstract string ShortenedName { get; }
+
///
/// The icon of this mod.
///
diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs
index ca120e446e..2b10d098f3 100644
--- a/osu.Game/Rulesets/Mods/ModAutoplay.cs
+++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs
@@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Mods
public class ModAutoplay : Mod
{
public override string Name => "Autoplay";
+ public override string ShortenedName => "AT";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_auto;
public override string Description => "Watch a perfect automated play through the song";
public override double ScoreMultiplier => 0;
diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs
index 332bd2c5ac..581fbc5e3a 100644
--- a/osu.Game/Rulesets/Mods/ModCinema.cs
+++ b/osu.Game/Rulesets/Mods/ModCinema.cs
@@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Mods
public class ModCinema : ModAutoplay
{
public override string Name => "Cinema";
+ public override string ShortenedName => "CN";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_cinema;
}
}
\ No newline at end of file
diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs
index 3e878d7104..cbad224316 100644
--- a/osu.Game/Rulesets/Mods/ModDaycore.cs
+++ b/osu.Game/Rulesets/Mods/ModDaycore.cs
@@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModDaycore : ModHalfTime
{
public override string Name => "Daycore";
+ public override string ShortenedName => "DC";
public override FontAwesome Icon => FontAwesome.fa_question;
public override string Description => "whoaaaaa";
diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs
index 1aab56a9fc..d8aa5ea1d2 100644
--- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs
+++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs
@@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public class ModDoubleTime : Mod, IApplicableToClock
{
public override string Name => "Double Time";
+ public override string ShortenedName => "DT";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_doubletime;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Zoooooooooom";
diff --git a/osu.Game/Rulesets/Mods/ModEasy.cs b/osu.Game/Rulesets/Mods/ModEasy.cs
index 075a62b0d7..aaf083fd9e 100644
--- a/osu.Game/Rulesets/Mods/ModEasy.cs
+++ b/osu.Game/Rulesets/Mods/ModEasy.cs
@@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModEasy : Mod, IApplicableToDifficulty
{
public override string Name => "Easy";
+ public override string ShortenedName => "EZ";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_easy;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => "Reduces overall difficulty - larger circles, more forgiving HP drain, less accuracy required.";
diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs
index b5ad859172..b7499e624a 100644
--- a/osu.Game/Rulesets/Mods/ModFlashlight.cs
+++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs
@@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModFlashlight : Mod
{
public override string Name => "Flashlight";
+ public override string ShortenedName => "FL";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_flashlight;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Restricted view area.";
diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs
index 14aede0809..a9e49bb4b0 100644
--- a/osu.Game/Rulesets/Mods/ModHalfTime.cs
+++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs
@@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModHalfTime : Mod, IApplicableToClock
{
public override string Name => "Half Time";
+ public override string ShortenedName => "HT";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_halftime;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => "Less zoom";
diff --git a/osu.Game/Rulesets/Mods/ModHardRock.cs b/osu.Game/Rulesets/Mods/ModHardRock.cs
index f33f46a207..36d82362e3 100644
--- a/osu.Game/Rulesets/Mods/ModHardRock.cs
+++ b/osu.Game/Rulesets/Mods/ModHardRock.cs
@@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModHardRock : Mod, IApplicableToDifficulty
{
public override string Name => "Hard Rock";
+ public override string ShortenedName => "HR";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hardrock;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Everything just got a bit harder...";
diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs
index 89b9b3b62d..25f6ad024d 100644
--- a/osu.Game/Rulesets/Mods/ModHidden.cs
+++ b/osu.Game/Rulesets/Mods/ModHidden.cs
@@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModHidden : Mod
{
public override string Name => "Hidden";
+ public override string ShortenedName => "HD";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
public override ModType Type => ModType.DifficultyIncrease;
public override bool Ranked => true;
diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs
index d04643fb8b..5cefd89023 100644
--- a/osu.Game/Rulesets/Mods/ModNightcore.cs
+++ b/osu.Game/Rulesets/Mods/ModNightcore.cs
@@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModNightcore : ModDoubleTime
{
public override string Name => "Nightcore";
+ public override string ShortenedName => "NC";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_nightcore;
public override string Description => "uguuuuuuuu";
diff --git a/osu.Game/Rulesets/Mods/ModNoFail.cs b/osu.Game/Rulesets/Mods/ModNoFail.cs
index d41c4e3956..3a3878d77e 100644
--- a/osu.Game/Rulesets/Mods/ModNoFail.cs
+++ b/osu.Game/Rulesets/Mods/ModNoFail.cs
@@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModNoFail : Mod
{
public override string Name => "NoFail";
+ public override string ShortenedName => "NF";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => "You can't fail, no matter what.";
diff --git a/osu.Game/Rulesets/Mods/ModPerfect.cs b/osu.Game/Rulesets/Mods/ModPerfect.cs
index 35217c8305..082370ea5d 100644
--- a/osu.Game/Rulesets/Mods/ModPerfect.cs
+++ b/osu.Game/Rulesets/Mods/ModPerfect.cs
@@ -6,6 +6,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModPerfect : ModSuddenDeath
{
public override string Name => "Perfect";
+ public override string ShortenedName => "PF";
public override string Description => "SS or quit.";
}
}
\ No newline at end of file
diff --git a/osu.Game/Rulesets/Mods/ModRelax.cs b/osu.Game/Rulesets/Mods/ModRelax.cs
index 5491f8fc58..a3f38e4ff6 100644
--- a/osu.Game/Rulesets/Mods/ModRelax.cs
+++ b/osu.Game/Rulesets/Mods/ModRelax.cs
@@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModRelax : Mod
{
public override string Name => "Relax";
+ public override string ShortenedName => "RX";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_relax;
public override double ScoreMultiplier => 0;
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModSuddenDeath) };
diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs
index 2bf0278046..999cb40f89 100644
--- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs
+++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs
@@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModSuddenDeath : Mod
{
public override string Name => "Sudden Death";
+ public override string ShortenedName => "SD";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_suddendeath;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Miss a note and fail.";
diff --git a/osu.Game/Rulesets/Mods/MultiMod.cs b/osu.Game/Rulesets/Mods/MultiMod.cs
index c5fac250d0..c40d107bda 100644
--- a/osu.Game/Rulesets/Mods/MultiMod.cs
+++ b/osu.Game/Rulesets/Mods/MultiMod.cs
@@ -6,6 +6,7 @@ namespace osu.Game.Rulesets.Mods
public class MultiMod : Mod
{
public override string Name => string.Empty;
+ public override string ShortenedName => string.Empty;
public override string Description => string.Empty;
public override double ScoreMultiplier => 0.0;
diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs
index c54aeb7852..33621662af 100644
--- a/osu.Game/Rulesets/Ruleset.cs
+++ b/osu.Game/Rulesets/Ruleset.cs
@@ -1,15 +1,17 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Game.Beatmaps;
-using osu.Game.Graphics;
-using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.UI;
+using System;
using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
-using osu.Game.Rulesets.Scoring;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics;
using osu.Game.Overlays.Settings;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets
{
@@ -19,9 +21,18 @@ namespace osu.Game.Rulesets
public virtual IEnumerable GetBeatmapStatistics(WorkingBeatmap beatmap) => new BeatmapStatistic[] { };
+ public IEnumerable GetAllMods() => Enum.GetValues(typeof(ModType)).Cast()
+ // Get all mod types as an IEnumerable
+ .SelectMany(GetModsFor)
+ // Confine all mods of each mod type into a single IEnumerable
+ .Where(mod => mod != null)
+ // Filter out all null mods
+ .SelectMany(mod => (mod as MultiMod)?.Mods ?? new[] { mod });
+ // Resolve MultiMods as their .Mods property
+
public abstract IEnumerable GetModsFor(ModType type);
- public abstract Mod GetAutoplayMod();
+ public Mod GetAutoplayMod() => GetAllMods().First(mod => mod is ModAutoplay);
protected Ruleset(RulesetInfo rulesetInfo)
{
diff --git a/osu.Game/Rulesets/Scoring/Score.cs b/osu.Game/Rulesets/Scoring/Score.cs
index 6169bb7380..ff4632a9c2 100644
--- a/osu.Game/Rulesets/Scoring/Score.cs
+++ b/osu.Game/Rulesets/Scoring/Score.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-using Newtonsoft.Json;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Users;
@@ -15,70 +14,30 @@ namespace osu.Game.Rulesets.Scoring
{
public ScoreRank Rank { get; set; }
- [JsonProperty(@"score")]
public double TotalScore { get; set; }
public double Accuracy { get; set; }
public double Health { get; set; } = 1;
- [JsonProperty(@"max_combo")]
public int MaxCombo { get; set; }
public int Combo { get; set; }
- [JsonProperty(@"mods")]
- protected string[] ModStrings { get; set; } //todo: parse to Mod objects
-
public RulesetInfo Ruleset { get; set; }
public Mod[] Mods { get; set; }
- [JsonProperty(@"user")]
public User User;
- [JsonProperty(@"replay_data")]
public Replay Replay;
public BeatmapInfo Beatmap;
- [JsonProperty(@"score_id")]
public long OnlineScoreID;
- [JsonProperty(@"created_at")]
public DateTimeOffset Date;
- [JsonProperty(@"statistics")]
- private Dictionary jsonStats
- {
- set
- {
- foreach (var kvp in value)
- {
- string key = kvp.Key;
- switch (key)
- {
- case @"count_300":
- key = @"300";
- break;
- case @"count_100":
- key = @"100";
- break;
- case @"count_50":
- key = @"50";
- break;
- case @"count_miss":
- key = @"x";
- break;
- default:
- continue;
- }
-
- Statistics.Add(key, kvp.Value);
- }
- }
- }
-
public Dictionary Statistics = new Dictionary();
}
}
diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs
index 986d8c92dc..47a39d4644 100644
--- a/osu.Game/Rulesets/UI/ModIcon.cs
+++ b/osu.Game/Rulesets/UI/ModIcon.cs
@@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.UI
private readonly SpriteIcon modIcon;
private readonly SpriteIcon background;
- private const float icon_size = 80;
+ private const float background_size = 80;
public FontAwesome Icon
{
@@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.UI
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
- Size = new Vector2(icon_size),
+ Size = new Vector2(background_size),
Icon = FontAwesome.fa_osu_mod_bg,
Shadow = true,
},
@@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.UI
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Colour = OsuColour.Gray(84),
- Size = new Vector2(icon_size - 35),
+ Size = new Vector2(background_size - 35),
Y = 25,
Icon = mod.Icon
},
diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs
index 9503ac1e0b..cae211fb9c 100644
--- a/osu.Game/Rulesets/UI/RulesetContainer.cs
+++ b/osu.Game/Rulesets/UI/RulesetContainer.cs
@@ -75,9 +75,6 @@ namespace osu.Game.Rulesets.UI
internal RulesetContainer(Ruleset ruleset)
{
Ruleset = ruleset;
-
- KeyBindingInputManager = CreateInputManager();
- KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
}
///
@@ -206,6 +203,9 @@ namespace osu.Game.Rulesets.UI
// Post-process the beatmap
processor.PostProcess(Beatmap);
+ KeyBindingInputManager = CreateInputManager();
+ KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
+
// Add mods, should always be the last thing applied to give full control to mods
applyMods(Mods);
}
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 9f33d624e2..be9098e3be 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -8,6 +8,11 @@ using osu.Framework.Screens;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Select;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Screens.Edit.Menus;
namespace osu.Game.Screens.Edit
{
@@ -17,6 +22,175 @@ namespace osu.Game.Screens.Edit
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
+ internal override bool ShowOverlays => false;
+
+ public Editor()
+ {
+ Add(new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 40,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = OsuColour.FromHex("111")
+ },
+ new EditorMenuBar
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ X = 100,
+ Items = new[]
+ {
+ new EditorMenuBarItem("File")
+ {
+ Items = new[]
+ {
+ new EditorMenuItem("Clear all notes"),
+ new EditorMenuItem("Open difficulty..."),
+ new EditorMenuItem("Save"),
+ new EditorMenuItem("Create new difficulty..."),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Revert to saved"),
+ new EditorMenuItem("Revert to saved (full"),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Test beatmap"),
+ new EditorMenuItem("Open AiMod"),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Upload Beatmap..."),
+ new EditorMenuItem("Export package"),
+ new EditorMenuItem("Export map package"),
+ new EditorMenuItem("Import from..."),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Open song folder"),
+ new EditorMenuItem("Open .osu in Notepad"),
+ new EditorMenuItem("Open .osb in Notepad"),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Exit", MenuItemType.Standard, Exit)
+ }
+ },
+ new EditorMenuBarItem("Edit")
+ {
+ Items = new[]
+ {
+ new EditorMenuItem("Undo"),
+ new EditorMenuItem("Redo"),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Cut"),
+ new EditorMenuItem("Copy"),
+ new EditorMenuItem("Paste"),
+ new EditorMenuItem("Delete"),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Select all"),
+ new EditorMenuItem("Clone"),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Reverse selection"),
+ new EditorMenuItem("Flip horizontally"),
+ new EditorMenuItem("Flip vertically"),
+ new EditorMenuItem("Rotate 90deg clockwise"),
+ new EditorMenuItem("Rotate 90deg anticlockwise"),
+ new EditorMenuItem("Rotate by..."),
+ new EditorMenuItem("Scale by..."),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Reset selected objects' samples"),
+ new EditorMenuItem("Reset all samples", MenuItemType.Destructive),
+ new EditorMenuItem("Reset combo colours", MenuItemType.Destructive),
+ new EditorMenuItem("Reset breaks", MenuItemType.Destructive),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Nudge backward"),
+ new EditorMenuItem("Nudge forward")
+ }
+ },
+ new EditorMenuBarItem("View")
+ {
+ Items = new[]
+ {
+ new EditorMenuItem("Compose"),
+ new EditorMenuItem("Design"),
+ new EditorMenuItem("Timing"),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Song setup..."),
+ new EditorMenuItem("Timing setup..."),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Volume"),
+ new EditorMenuItem("Grid level"),
+ new EditorMenuItem("Show video"),
+ new EditorMenuItem("Show sample name"),
+ new EditorMenuItem("Snaking sliders"),
+ new EditorMenuItem("Hit animations"),
+ new EditorMenuItem("Follow points"),
+ new EditorMenuItem("Stacking")
+ }
+ },
+ new EditorMenuBarItem("Compose")
+ {
+ Items = new[]
+ {
+ new EditorMenuItem("Snap divisor"),
+ new EditorMenuItem("Audio rate"),
+ new EditorMenuItem("Grid snapping"),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Create polygon cricles..."),
+ new EditorMenuItem("Convert slider to stream"),
+ new EditorMenuItem("Enable live mapping mode"),
+ new EditorMenuItem("Sample import")
+ }
+ },
+ new EditorMenuBarItem("Design")
+ {
+ Items = new[]
+ {
+ new EditorMenuItem("Move all elements in time...")
+ }
+ },
+ new EditorMenuBarItem("Timing")
+ {
+ Items = new[]
+ {
+ new EditorMenuItem("Time signature"),
+ new EditorMenuItem("Metronome clicks"),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Add timing section"),
+ new EditorMenuItem("Add inheriting section"),
+ new EditorMenuItem("Reset current section"),
+ new EditorMenuItem("Delete timing section"),
+ new EditorMenuItem("Resnap current section"),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Timing setup..."),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Resnap all notes", MenuItemType.Destructive),
+ new EditorMenuItem("Move all notes in time...", MenuItemType.Destructive),
+ new EditorMenuItem("Recalculate slider lengths", MenuItemType.Destructive),
+ new EditorMenuItem("Delete all timing sections", MenuItemType.Destructive),
+ new EditorMenuItemSpacer(),
+ new EditorMenuItem("Set current position as preview point")
+ }
+ },
+ new EditorMenuBarItem("Web")
+ {
+ Items = new[]
+ {
+ new EditorMenuItem("This Beatmap's information page"),
+ new EditorMenuItem("This Beatmap's thread"),
+ new EditorMenuItem("Quick reply")
+ }
+ },
+ new EditorMenuBarItem("Help")
+ {
+ Items = new[]
+ {
+ new EditorMenuItem("Show in-game help"),
+ new EditorMenuItem("View FAQ")
+ }
+ }
+ }
+ }
+ }
+ });
+ }
+
protected override void OnResuming(Screen last)
{
Beatmap.Value.Track?.Stop();
diff --git a/osu.Game/Screens/Edit/Menus/EditorMenuBar.cs b/osu.Game/Screens/Edit/Menus/EditorMenuBar.cs
new file mode 100644
index 0000000000..bb349b1531
--- /dev/null
+++ b/osu.Game/Screens/Edit/Menus/EditorMenuBar.cs
@@ -0,0 +1,122 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Input;
+using osu.Game.Graphics;
+using osu.Game.Graphics.UserInterface;
+using OpenTK;
+using OpenTK.Graphics;
+
+namespace osu.Game.Screens.Edit.Menus
+{
+ public class EditorMenuBar : OsuMenu
+ {
+ public EditorMenuBar()
+ : base(Direction.Horizontal, true)
+ {
+ ItemsContainer.Padding = new MarginPadding(0);
+ BackgroundColour = Color4.Transparent;
+ }
+
+ protected override Framework.Graphics.UserInterface.Menu CreateSubMenu() => new SubMenu();
+
+ protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableEditorBarMenuItem(item);
+
+ private class DrawableEditorBarMenuItem : DrawableOsuMenuItem
+ {
+ private Color4 openedForegroundColour;
+ private Color4 openedBackgroundColour;
+
+ public DrawableEditorBarMenuItem(MenuItem item)
+ : base(item)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ ForegroundColour = ForegroundColourHover = colours.BlueLight;
+ BackgroundColour = BackgroundColourHover = Color4.Transparent;
+ openedForegroundColour = Color4.White;
+ openedBackgroundColour = colours.Gray3;
+ }
+
+ protected override void UpdateBackgroundColour()
+ {
+ if (State == MenuItemState.Selected)
+ Background.FadeColour(openedBackgroundColour);
+ else
+ base.UpdateBackgroundColour();
+ }
+
+ protected override void UpdateForegroundColour()
+ {
+ if (State == MenuItemState.Selected)
+ Foreground.FadeColour(openedForegroundColour);
+ else
+ base.UpdateForegroundColour();
+ }
+
+ protected override Drawable CreateBackground() => new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ Child = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Height = 2,
+ Masking = true,
+ CornerRadius = 4,
+ Child = new Box { RelativeSizeAxes = Axes.Both }
+ }
+ };
+ }
+
+ private class SubMenu : OsuMenu
+ {
+ public SubMenu()
+ : base(Direction.Vertical)
+ {
+ OriginPosition = new Vector2(5, 1);
+ ItemsContainer.Padding = new MarginPadding { Top = 5, Bottom = 5 };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ BackgroundColour = colours.Gray3;
+ }
+
+ protected override Framework.Graphics.UserInterface.Menu CreateSubMenu() => new SubMenu();
+
+ protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableSubMenuItem(item);
+
+ private class DrawableSubMenuItem : DrawableOsuMenuItem
+ {
+ public DrawableSubMenuItem(MenuItem item)
+ : base(item)
+ {
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ if (Item is EditorMenuItemSpacer)
+ return true;
+ return base.OnHover(state);
+ }
+
+ protected override bool OnClick(InputState state)
+ {
+ if (Item is EditorMenuItemSpacer)
+ return true;
+ return base.OnClick(state);
+ }
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Edit/Menus/EditorMenuBarItem.cs b/osu.Game/Screens/Edit/Menus/EditorMenuBarItem.cs
new file mode 100644
index 0000000000..201bc6e5c3
--- /dev/null
+++ b/osu.Game/Screens/Edit/Menus/EditorMenuBarItem.cs
@@ -0,0 +1,15 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics.UserInterface;
+
+namespace osu.Game.Screens.Edit.Menus
+{
+ public class EditorMenuBarItem : MenuItem
+ {
+ public EditorMenuBarItem(string text)
+ : base(text)
+ {
+ }
+ }
+}
diff --git a/osu.Game/Screens/Edit/Menus/EditorMenuItem.cs b/osu.Game/Screens/Edit/Menus/EditorMenuItem.cs
new file mode 100644
index 0000000000..c7e36522cf
--- /dev/null
+++ b/osu.Game/Screens/Edit/Menus/EditorMenuItem.cs
@@ -0,0 +1,23 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Game.Graphics.UserInterface;
+
+namespace osu.Game.Screens.Edit.Menus
+{
+ public class EditorMenuItem : OsuMenuItem
+ {
+ private const int min_text_length = 40;
+
+ public EditorMenuItem(string text, MenuItemType type = MenuItemType.Standard)
+ : base(text.PadRight(min_text_length), type)
+ {
+ }
+
+ public EditorMenuItem(string text, MenuItemType type, Action action)
+ : base(text.PadRight(min_text_length), type, action)
+ {
+ }
+ }
+}
diff --git a/osu.Game/Screens/Edit/Menus/EditorMenuItemSpacer.cs b/osu.Game/Screens/Edit/Menus/EditorMenuItemSpacer.cs
new file mode 100644
index 0000000000..5060165ef7
--- /dev/null
+++ b/osu.Game/Screens/Edit/Menus/EditorMenuItemSpacer.cs
@@ -0,0 +1,13 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Screens.Edit.Menus
+{
+ public class EditorMenuItemSpacer : EditorMenuItem
+ {
+ public EditorMenuItemSpacer()
+ : base(" ")
+ {
+ }
+ }
+}
diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs
index 0898c079ce..aca169c3dc 100644
--- a/osu.Game/Screens/Menu/Button.cs
+++ b/osu.Game/Screens/Menu/Button.cs
@@ -28,6 +28,8 @@ namespace osu.Game.Screens.Menu
///
public class Button : BeatSyncedContainer, IStateful
{
+ public event Action StateChanged;
+
private readonly Container iconText;
private readonly Container box;
private readonly Box boxHoverLayer;
@@ -266,6 +268,8 @@ namespace osu.Game.Screens.Menu
this.FadeOut(explode_duration / 4f * 3);
break;
}
+
+ StateChanged?.Invoke(State);
}
}
}
diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs
index 71f2a16c09..e4dbe00a80 100644
--- a/osu.Game/Screens/Menu/ButtonSystem.cs
+++ b/osu.Game/Screens/Menu/ButtonSystem.cs
@@ -22,6 +22,8 @@ namespace osu.Game.Screens.Menu
{
public class ButtonSystem : Container, IStateful
{
+ public event Action StateChanged;
+
public Action OnEdit;
public Action OnExit;
public Action OnDirect;
@@ -294,6 +296,8 @@ namespace osu.Game.Screens.Menu
backButton.State = state == MenuState.Play ? ButtonState.Expanded : ButtonState.Contracted;
settingsButton.State = state == MenuState.TopLevel ? ButtonState.Expanded : ButtonState.Contracted;
}
+
+ StateChanged?.Invoke(State);
}
}
diff --git a/osu.Game/Screens/Play/KeyCounterCollection.cs b/osu.Game/Screens/Play/KeyCounterCollection.cs
index 0f6c5984c4..d21be71785 100644
--- a/osu.Game/Screens/Play/KeyCounterCollection.cs
+++ b/osu.Game/Screens/Play/KeyCounterCollection.cs
@@ -28,6 +28,8 @@ namespace osu.Game.Screens.Play
public override void Add(KeyCounter key)
{
+ if (key == null) throw new ArgumentNullException(nameof(key));
+
base.Add(key);
key.IsCounting = IsCounting;
key.FadeTime = FadeTime;
diff --git a/osu.Game/Screens/Play/SkipButton.cs b/osu.Game/Screens/Play/SkipButton.cs
index 3cf371f1e1..6519a8db36 100644
--- a/osu.Game/Screens/Play/SkipButton.cs
+++ b/osu.Game/Screens/Play/SkipButton.cs
@@ -133,6 +133,8 @@ namespace osu.Game.Screens.Play
private class FadeContainer : Container, IStateful
{
+ public event Action StateChanged;
+
private Visibility state;
private ScheduledDelegate scheduledHide;
@@ -144,8 +146,10 @@ namespace osu.Game.Screens.Play
}
set
{
- var lastState = state;
+ if (state == value)
+ return;
+ var lastState = state;
state = value;
scheduledHide?.Cancel();
@@ -164,6 +168,8 @@ namespace osu.Game.Screens.Play
this.FadeOut(1000, Easing.OutExpo);
break;
}
+
+ StateChanged?.Invoke(State);
}
}
diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs
index f3bb523611..81dbf3eca4 100644
--- a/osu.Game/Screens/Play/SquareGraph.cs
+++ b/osu.Game/Screens/Play/SquareGraph.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 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 osu.Framework;
@@ -170,6 +171,8 @@ namespace osu.Game.Screens.Play
private const float padding = 2;
public const float WIDTH = cube_size + padding;
+ public event Action StateChanged;
+
private readonly List drawableRows = new List();
private float filled;
@@ -186,6 +189,7 @@ namespace osu.Game.Screens.Play
}
private ColumnState state;
+
public ColumnState State
{
get { return state; }
@@ -196,6 +200,8 @@ namespace osu.Game.Screens.Play
if (IsLoaded)
fillActive();
+
+ StateChanged?.Invoke(State);
}
}
diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs
index 47d25585ad..a676516300 100644
--- a/osu.Game/Screens/Select/BeatmapDetailArea.cs
+++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
@@ -10,6 +11,8 @@ namespace osu.Game.Screens.Select
{
public class BeatmapDetailArea : Container
{
+ private const float details_padding = 10;
+
private readonly Container content;
protected override Container Content => content;
@@ -66,9 +69,8 @@ namespace osu.Game.Screens.Select
Details = new BeatmapDetails
{
RelativeSizeAxes = Axes.X,
- Masking = true,
- Height = 352,
Alpha = 0,
+ Margin = new MarginPadding { Top = details_padding },
},
Leaderboard = new Leaderboard
{
@@ -76,5 +78,12 @@ namespace osu.Game.Screens.Select
}
});
}
+
+ protected override void UpdateAfterChildren()
+ {
+ base.UpdateAfterChildren();
+
+ Details.Height = Math.Min(DrawHeight - details_padding * 3 - BeatmapDetailAreaTabControl.HEIGHT, 450);
+ }
}
}
diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs
index a5486efa96..a98362e89c 100644
--- a/osu.Game/Screens/Select/BeatmapDetails.cs
+++ b/osu.Game/Screens/Select/BeatmapDetails.cs
@@ -9,71 +9,189 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
-using System.Globalization;
using System.Linq;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Framework.Threading;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Game.Screens.Select.Details;
using osu.Game.Beatmaps;
namespace osu.Game.Screens.Select
{
public class BeatmapDetails : Container
{
- private readonly MetadataSegment description;
- private readonly MetadataSegment source;
- private readonly MetadataSegment tags;
+ private const float spacing = 10;
+ private const float transition_duration = 250;
- private readonly DifficultyRow circleSize;
- private readonly DifficultyRow drainRate;
- private readonly DifficultyRow overallDifficulty;
- private readonly DifficultyRow approachRate;
- private readonly DifficultyRow stars;
+ private readonly FillFlowContainer top, statsFlow;
+ private readonly AdvancedStats advanced;
+ private readonly DetailBox ratingsContainer;
+ private readonly UserRatings ratings;
+ private readonly ScrollContainer metadataScroll;
+ private readonly MetadataSection description, source, tags;
+ private readonly Container failRetryContainer;
+ private readonly FailRetryGraph failRetryGraph;
+ private readonly DimmedLoadingAnimation loading;
- private readonly Container ratingsContainer;
- private readonly Bar ratingsBar;
- private readonly OsuSpriteText negativeRatings;
- private readonly OsuSpriteText positiveRatings;
- private readonly BarGraph ratingsGraph;
-
- private readonly FillFlowContainer retryFailContainer;
- private readonly BarGraph retryGraph;
- private readonly BarGraph failGraph;
+ private APIAccess api;
private ScheduledDelegate pendingBeatmapSwitch;
- private BeatmapInfo beatmap;
+ private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get { return beatmap; }
-
set
{
- if (beatmap == value) return;
-
+ if (value == beatmap) return;
beatmap = value;
pendingBeatmapSwitch?.Cancel();
- pendingBeatmapSwitch = Schedule(updateStats);
+ pendingBeatmapSwitch = Schedule(updateStatistics);
}
}
- private void updateStats()
+ public BeatmapDetails()
{
- if (beatmap == null) return;
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black.Opacity(0.5f),
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Horizontal = spacing },
+ Children = new Drawable[]
+ {
+ top = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Horizontal,
+ Children = new Drawable[]
+ {
+ statsFlow = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Width = 0.5f,
+ Spacing = new Vector2(spacing),
+ Padding = new MarginPadding { Right = spacing / 2 },
+ Children = new[]
+ {
+ new DetailBox
+ {
+ Child = advanced = new AdvancedStats
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Padding = new MarginPadding { Horizontal = spacing, Top = spacing * 2, Bottom = spacing },
+ },
+ },
+ ratingsContainer = new DetailBox
+ {
+ Child = ratings = new UserRatings
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 134,
+ Padding = new MarginPadding { Horizontal = spacing, Top = spacing },
+ },
+ },
+ },
+ },
+ metadataScroll = new ScrollContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ Width = 0.5f,
+ ScrollbarVisible = false,
+ Padding = new MarginPadding { Left = spacing / 2 },
+ Child = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ LayoutDuration = transition_duration,
+ Spacing = new Vector2(spacing * 2),
+ Margin = new MarginPadding { Top = spacing * 2 },
+ Children = new[]
+ {
+ description = new MetadataSection("Description")
+ {
+ TextColour = Color4.White.Opacity(0.75f),
+ },
+ source = new MetadataSection("Source")
+ {
+ TextColour = Color4.White.Opacity(0.75f),
+ },
+ tags = new MetadataSection("Tags"),
+ },
+ },
+ },
+ },
+ },
+ failRetryContainer = new Container
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ RelativeSizeAxes = Axes.X,
+ Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Text = "Points of Failure",
+ Font = @"Exo2.0-Bold",
+ TextSize = 14,
+ },
+ failRetryGraph = new FailRetryGraph
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Top = 14 + spacing / 2 },
+ },
+ },
+ },
+ },
+ },
+ loading = new DimmedLoadingAnimation
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ };
+ }
- description.Text = beatmap.Version;
- source.Text = beatmap.Metadata.Source;
- tags.Text = beatmap.Metadata.Tags;
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours, APIAccess api)
+ {
+ this.api = api;
+ tags.TextColour = colours.Yellow;
+ }
- circleSize.Value = beatmap.Difficulty.CircleSize;
- drainRate.Value = beatmap.Difficulty.DrainRate;
- overallDifficulty.Value = beatmap.Difficulty.OverallDifficulty;
- approachRate.Value = beatmap.Difficulty.ApproachRate;
- stars.Value = (float)beatmap.StarDifficulty;
+ protected override void UpdateAfterChildren()
+ {
+ base.UpdateAfterChildren();
- var requestedBeatmap = beatmap;
+ metadataScroll.Height = statsFlow.DrawHeight;
+ failRetryContainer.Height = DrawHeight - Padding.TotalVertical - (top.DrawHeight + spacing / 2);
+ }
+
+ private void updateStatistics()
+ {
+ if (Beatmap == null)
+ {
+ clearStats();
+ return;
+ }
+
+ ratingsContainer.FadeIn(transition_duration);
+ advanced.Beatmap = Beatmap;
+ description.Text = Beatmap.Version;
+ source.Text = Beatmap.Metadata.Source;
+ tags.Text = Beatmap.Metadata.Tags;
+
+ var requestedBeatmap = Beatmap;
if (requestedBeatmap.Metrics == null)
{
var lookup = new GetBeatmapDetailsRequest(requestedBeatmap);
@@ -84,413 +202,195 @@ namespace osu.Game.Screens.Select
return;
requestedBeatmap.Metrics = res;
- Schedule(() => updateMetrics(res));
+ Schedule(() => displayMetrics(res));
};
- lookup.Failure += e => Schedule(() => updateMetrics(null));
+ lookup.Failure += e => Schedule(() => displayMetrics(null));
api.Queue(lookup);
loading.Show();
}
- updateMetrics(requestedBeatmap.Metrics, false);
+ displayMetrics(requestedBeatmap.Metrics, false);
}
- ///
- /// Update displayed metrics.
- ///
- /// New metrics to overwrite the existing display. Can be null.
- /// Whether to hide the display on null or empty metrics. If false, we will dim as if waiting for further updates.
- private void updateMetrics(BeatmapMetrics metrics, bool failOnMissing = true)
+ private void displayMetrics(BeatmapMetrics metrics, bool failOnMissing = true)
{
- var hasRatings = metrics?.Ratings.Any() ?? false;
- var hasRetriesFails = (metrics?.Retries.Any() ?? false) && metrics.Fails.Any();
+ var hasRatings = metrics?.Ratings?.Any() ?? false;
+ var hasRetriesFails = (metrics?.Retries?.Any() ?? false) && (metrics.Fails?.Any() ?? false);
- if (failOnMissing)
- loading.Hide();
+ if (failOnMissing) loading.Hide();
if (hasRatings)
{
- var ratings = metrics.Ratings.ToList();
- ratingsContainer.Show();
-
- negativeRatings.Text = ratings.GetRange(0, ratings.Count / 2).Sum().ToString();
- positiveRatings.Text = ratings.GetRange(ratings.Count / 2, ratings.Count / 2).Sum().ToString();
- ratingsBar.Length = (float)ratings.GetRange(0, ratings.Count / 2).Sum() / ratings.Sum();
-
- ratingsGraph.Values = ratings.Select(rating => (float)rating);
-
- ratingsContainer.FadeColour(Color4.White, 500, Easing.Out);
+ ratings.Metrics = metrics;
+ ratings.FadeIn(transition_duration);
}
else if (failOnMissing)
- ratingsGraph.Values = new float[10];
+ {
+ ratings.Metrics = new BeatmapMetrics
+ {
+ Ratings = new int[10],
+ };
+ }
else
- ratingsContainer.FadeColour(Color4.Gray, 500, Easing.Out);
+ {
+ ratings.FadeTo(0.25f, transition_duration);
+ }
if (hasRetriesFails)
{
- var retries = metrics.Retries;
- var fails = metrics.Fails;
- retryFailContainer.Show();
-
- float maxValue = fails.Zip(retries, (fail, retry) => fail + retry).Max();
- failGraph.MaxValue = maxValue;
- retryGraph.MaxValue = maxValue;
-
- failGraph.Values = fails.Select(fail => (float)fail);
- retryGraph.Values = retries.Zip(fails, (retry, fail) => retry + MathHelper.Clamp(fail, 0, maxValue));
-
- retryFailContainer.FadeColour(Color4.White, 500, Easing.Out);
+ failRetryGraph.Metrics = metrics;
+ failRetryContainer.FadeIn(transition_duration);
}
else if (failOnMissing)
{
- failGraph.Values = new float[100];
- retryGraph.Values = new float[100];
+ failRetryGraph.Metrics = new BeatmapMetrics
+ {
+ Fails = new int[100],
+ Retries = new int[100],
+ };
}
else
- retryFailContainer.FadeColour(Color4.Gray, 500, Easing.Out);
+ {
+ failRetryContainer.FadeTo(0.25f, transition_duration);
+ }
}
- public BeatmapDetails()
+ private void clearStats()
{
- Children = new Drawable[]
+ description.Text = null;
+ source.Text = null;
+ tags.Text = null;
+ advanced.Beatmap = new BeatmapInfo
{
- new Box
+ StarDifficulty = 0,
+ Difficulty = new BeatmapDifficulty
{
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
- Alpha = 0.5f,
+ CircleSize = 0,
+ DrainRate = 0,
+ OverallDifficulty = 0,
+ ApproachRate = 0,
},
- new FillFlowContainer
- {
- Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight,
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Width = 0.4f,
- Direction = FillDirection.Vertical,
- LayoutDuration = 200,
- LayoutEasing = Easing.OutQuint,
- Children = new[]
- {
- description = new MetadataSegment("Description"),
- source = new MetadataSegment("Source"),
- tags = new MetadataSegment("Tags")
- },
- },
- new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Width = 0.6f,
- Direction = FillDirection.Vertical,
- Spacing = new Vector2(0, 15),
- Padding = new MarginPadding(10) { Top = 0 },
- Children = new Drawable[]
- {
- new Container
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Children = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
- Alpha = 0.5f,
- },
- new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Direction = FillDirection.Vertical,
- Spacing = new Vector2(0, 5),
- Padding = new MarginPadding(10),
- Children = new[]
- {
- circleSize = new DifficultyRow("Circle Size", 7),
- drainRate = new DifficultyRow("HP Drain"),
- overallDifficulty = new DifficultyRow("Accuracy"),
- approachRate = new DifficultyRow("Approach Rate"),
- stars = new DifficultyRow("Star Difficulty"),
- },
- },
- },
- },
- ratingsContainer = new Container
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Alpha = 0,
- AlwaysPresent = true,
- Children = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
- Alpha = 0.5f,
- },
- new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Direction = FillDirection.Vertical,
- Padding = new MarginPadding
- {
- Top = 25,
- Left = 15,
- Right = 15,
- },
- Children = new Drawable[]
- {
- new OsuSpriteText
- {
- Text = "User Rating",
- Font = @"Exo2.0-Medium",
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- },
- ratingsBar = new Bar
- {
- RelativeSizeAxes = Axes.X,
- Height = 5,
- },
- new Container
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Children = new[]
- {
- negativeRatings = new OsuSpriteText
- {
- Font = @"Exo2.0-Regular",
- Text = "0",
- },
- positiveRatings = new OsuSpriteText
- {
- Font = @"Exo2.0-Regular",
- Text = "0",
- Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight,
- },
- },
- },
- new OsuSpriteText
- {
- Text = "Rating Spread",
- TextSize = 14,
- Font = @"Exo2.0-Regular",
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- },
- ratingsGraph = new BarGraph
- {
- RelativeSizeAxes = Axes.X,
- Height = 50,
- },
- },
- },
- },
- },
- retryFailContainer = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Alpha = 0,
- Children = new Drawable[]
- {
- new OsuSpriteText
- {
- Text = "Points of Failure",
- Font = @"Exo2.0-Regular",
- },
- new Container
- {
- RelativeSizeAxes = Axes.X,
- Size = new Vector2(1 / 0.6f, 50),
- Children = new[]
- {
- retryGraph = new BarGraph
- {
- RelativeSizeAxes = Axes.Both,
- },
- failGraph = new BarGraph
- {
- RelativeSizeAxes = Axes.Both,
- },
- },
- },
- }
- },
- },
- },
- loading = new LoadingAnimation()
};
+
+ loading.Hide();
+ ratingsContainer.FadeOut(transition_duration);
+ failRetryContainer.FadeOut(transition_duration);
}
- private APIAccess api;
- private readonly LoadingAnimation loading;
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colour, APIAccess api)
+ private class DetailBox : Container
{
- this.api = api;
+ private readonly Container content;
+ protected override Container Content => content;
- description.AccentColour = colour.GrayB;
- source.AccentColour = colour.GrayB;
- tags.AccentColour = colour.YellowLight;
-
- stars.AccentColour = colour.Yellow;
-
- ratingsBar.BackgroundColour = colour.Green;
- ratingsBar.AccentColour = colour.YellowDark;
- ratingsGraph.Colour = colour.BlueDark;
-
- failGraph.Colour = colour.YellowDarker;
- retryGraph.Colour = colour.Yellow;
- }
-
- private class DifficultyRow : Container, IHasAccentColour
- {
- private readonly OsuSpriteText name;
- private readonly Bar bar;
- private readonly OsuSpriteText valueText;
-
- private readonly float maxValue;
-
- private float difficultyValue;
- public float Value
+ public DetailBox()
{
- get
- {
- return difficultyValue;
- }
- set
- {
- difficultyValue = value;
- bar.Length = value / maxValue;
- valueText.Text = value.ToString("N1", CultureInfo.CurrentCulture);
- }
- }
-
- public Color4 AccentColour
- {
- get
- {
- return bar.AccentColour;
- }
- set
- {
- bar.AccentColour = value;
- }
- }
-
- public DifficultyRow(string difficultyName, float maxValue = 10)
- {
- this.maxValue = maxValue;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
- Children = new Drawable[]
+
+ InternalChildren = new Drawable[]
{
- name = new OsuSpriteText
+ new Box
{
- Font = @"Exo2.0-Regular",
- Text = difficultyName,
- },
- bar = new Bar
- {
- Origin = Anchor.CentreLeft,
- Anchor = Anchor.CentreLeft,
RelativeSizeAxes = Axes.Both,
- Size = new Vector2(1, 0.35f),
- Padding = new MarginPadding { Left = 100, Right = 25 },
+ Colour = Color4.Black.Opacity(0.5f),
},
- valueText = new OsuSpriteText
+ content = new Container
{
- Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight,
- Font = @"Exo2.0-Regular",
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
},
};
}
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colour)
- {
- name.Colour = colour.GrayB;
- bar.BackgroundColour = colour.Gray7;
- valueText.Colour = colour.GrayB;
- }
}
- private class MetadataSegment : Container, IHasAccentColour
+ private class MetadataSection : Container
{
- private readonly OsuSpriteText header;
- private readonly FillFlowContainer content;
+ private readonly TextFlowContainer textFlow;
public string Text
{
set
{
if (string.IsNullOrEmpty(value))
- Hide();
- else
{
- Show();
- if (header.Text == "Tags")
- content.ChildrenEnumerable = value.Split(' ').Select(text => new OsuSpriteText
- {
- Text = text,
- Font = "Exo2.0-Regular",
- });
- else
- content.Children = new[]
- {
- new OsuSpriteText
- {
- Text = value,
- Font = "Exo2.0-Regular",
- }
- };
+ this.FadeOut(transition_duration);
+ return;
}
+
+ this.FadeIn(transition_duration);
+ textFlow.Clear();
+ textFlow.AddText(value, s => s.TextSize = 14);
}
}
- public Color4 AccentColour
+ public Color4 TextColour
{
- get
- {
- return content.Colour;
- }
- set
- {
- content.Colour = value;
- }
+ get { return textFlow.Colour; }
+ set { textFlow.Colour = value; }
}
- public MetadataSegment(string headerText)
+ public MetadataSection(string title)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
- Margin = new MarginPadding { Top = 10 };
+
+ InternalChild = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Spacing = new Vector2(spacing / 2),
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Child = new OsuSpriteText
+ {
+ Text = title,
+ Font = @"Exo2.0-Bold",
+ TextSize = 14,
+ },
+ },
+ textFlow = new TextFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ },
+ },
+ };
+ }
+ }
+
+ private class DimmedLoadingAnimation : VisibilityContainer
+ {
+ private readonly LoadingAnimation loading;
+
+ public DimmedLoadingAnimation()
+ {
Children = new Drawable[]
{
- header = new OsuSpriteText
+ new Box
{
- Font = @"Exo2.0-Bold",
- Text = headerText,
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black.Opacity(0.5f),
},
- content = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Direction = FillDirection.Full,
- Spacing = new Vector2(5, 0),
- Margin = new MarginPadding { Top = header.TextSize }
- }
+ loading = new LoadingAnimation(),
};
}
+
+ protected override void PopIn()
+ {
+ this.FadeIn(transition_duration, Easing.OutQuint);
+ loading.State = Visibility.Visible;
+ }
+
+ protected override void PopOut()
+ {
+ this.FadeOut(transition_duration, Easing.OutQuint);
+ loading.State = Visibility.Hidden;
+ }
}
}
}
diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs
new file mode 100644
index 0000000000..d92c8ed509
--- /dev/null
+++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs
@@ -0,0 +1,152 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using OpenTK;
+using OpenTK.Graphics;
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using System;
+using osu.Game.Beatmaps;
+
+namespace osu.Game.Screens.Select.Details
+{
+ public class AdvancedStats : Container
+ {
+ private readonly StatisticRow firstValue, hpDrain, accuracy, approachRate, starDifficulty;
+
+ private BeatmapInfo beatmap;
+ public BeatmapInfo Beatmap
+ {
+ get { return beatmap; }
+ set
+ {
+ if (value == beatmap) return;
+ beatmap = value;
+
+ //mania specific
+ if ((Beatmap?.Ruleset?.ID ?? 0) == 3)
+ {
+ firstValue.Title = "Key Amount";
+ firstValue.Value = (int)Math.Round(Beatmap?.Difficulty?.CircleSize ?? 0);
+ }
+ else
+ {
+ firstValue.Title = "Circle Size";
+ firstValue.Value = Beatmap?.Difficulty?.CircleSize ?? 0;
+ }
+
+ hpDrain.Value = beatmap.Difficulty.DrainRate;
+ accuracy.Value = beatmap.Difficulty.OverallDifficulty;
+ approachRate.Value = beatmap.Difficulty.ApproachRate;
+ starDifficulty.Value = (float)beatmap.StarDifficulty;
+ }
+ }
+
+ public AdvancedStats()
+ {
+ Child = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Spacing = new Vector2(4f),
+ Children = new[]
+ {
+ firstValue = new StatisticRow(), //circle size/key amount
+ hpDrain = new StatisticRow { Title = "HP Drain" },
+ accuracy = new StatisticRow { Title = "Accuracy" },
+ approachRate = new StatisticRow { Title = "Approach Rate" },
+ starDifficulty = new StatisticRow(10, true) { Title = "Star Difficulty" },
+ },
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ starDifficulty.AccentColour = colours.Yellow;
+ }
+
+ private class StatisticRow : Container, IHasAccentColour
+ {
+ private const float value_width = 25;
+ private const float name_width = 70;
+
+ private readonly float maxValue;
+ private readonly bool forceDecimalPlaces;
+ private readonly OsuSpriteText name, value;
+ private readonly Bar bar;
+
+ public string Title
+ {
+ get { return name.Text; }
+ set { name.Text = value; }
+ }
+
+ private float difficultyValue;
+ public float Value
+ {
+ get { return difficultyValue; }
+ set
+ {
+ difficultyValue = value;
+ bar.Length = value / maxValue;
+ this.value.Text = value.ToString(forceDecimalPlaces ? "0.00" : "0.##");
+ }
+ }
+
+ public Color4 AccentColour
+ {
+ get { return bar.AccentColour; }
+ set { bar.AccentColour = value; }
+ }
+
+ public StatisticRow(float maxValue = 10, bool forceDecimalPlaces = false)
+ {
+ this.maxValue = maxValue;
+ this.forceDecimalPlaces = forceDecimalPlaces;
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ Width = name_width,
+ AutoSizeAxes = Axes.Y,
+ Child = name = new OsuSpriteText
+ {
+ TextSize = 13,
+ },
+ },
+ bar = new Bar
+ {
+ Origin = Anchor.CentreLeft,
+ Anchor = Anchor.CentreLeft,
+ RelativeSizeAxes = Axes.X,
+ Height = 5,
+ BackgroundColour = Color4.White.Opacity(0.5f),
+ Padding = new MarginPadding { Left = name_width + 10, Right = value_width + 10 },
+ },
+ new Container
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ Width = value_width,
+ RelativeSizeAxes = Axes.Y,
+ Child = value = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ TextSize = 13,
+ },
+ },
+ };
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Select/Details/FailRetryGraph.cs b/osu.Game/Screens/Select/Details/FailRetryGraph.cs
new file mode 100644
index 0000000000..4d75c49d17
--- /dev/null
+++ b/osu.Game/Screens/Select/Details/FailRetryGraph.cs
@@ -0,0 +1,62 @@
+// Copyright (c) 2007-2017 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;
+using osu.Game.Graphics;
+using osu.Game.Graphics.UserInterface;
+using System.Linq;
+using osu.Game.Beatmaps;
+
+namespace osu.Game.Screens.Select.Details
+{
+ public class FailRetryGraph : Container
+ {
+ private readonly BarGraph retryGraph, failGraph;
+
+ private BeatmapMetrics metrics;
+ public BeatmapMetrics Metrics
+ {
+ get { return metrics; }
+ set
+ {
+ if (value == metrics) return;
+ metrics = value;
+
+ var retries = Metrics.Retries;
+ var fails = Metrics.Fails;
+
+ float maxValue = fails.Zip(retries, (fail, retry) => fail + retry).Max();
+ failGraph.MaxValue = maxValue;
+ retryGraph.MaxValue = maxValue;
+
+ failGraph.Values = fails.Select(f => (float)f);
+ retryGraph.Values = retries.Zip(fails, (retry, fail) => retry + MathHelper.Clamp(fail, 0, maxValue));
+ }
+ }
+
+ public FailRetryGraph()
+ {
+ Children = new[]
+ {
+ retryGraph = new BarGraph
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ failGraph = new BarGraph
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ retryGraph.Colour = colours.Yellow;
+ failGraph.Colour = colours.YellowDarker;
+ }
+ }
+}
diff --git a/osu.Game/Screens/Select/Details/UserRatings.cs b/osu.Game/Screens/Select/Details/UserRatings.cs
new file mode 100644
index 0000000000..db6d932464
--- /dev/null
+++ b/osu.Game/Screens/Select/Details/UserRatings.cs
@@ -0,0 +1,122 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using System.Linq;
+using osu.Game.Beatmaps;
+
+namespace osu.Game.Screens.Select.Details
+{
+ public class UserRatings : Container
+ {
+ private readonly FillFlowContainer header;
+ private readonly Bar ratingsBar;
+ private readonly OsuSpriteText negativeRatings, positiveRatings;
+ private readonly Container graphContainer;
+ private readonly BarGraph graph;
+
+ private BeatmapMetrics metrics;
+ public BeatmapMetrics Metrics
+ {
+ get { return metrics; }
+ set
+ {
+ if (value == metrics) return;
+ metrics = value;
+
+ var ratings = Metrics.Ratings.ToList();
+ negativeRatings.Text = ratings.GetRange(0, ratings.Count / 2).Sum().ToString();
+ positiveRatings.Text = ratings.GetRange(ratings.Count / 2, ratings.Count / 2).Sum().ToString();
+ ratingsBar.Length = (float)ratings.GetRange(0, ratings.Count / 2).Sum() / ratings.Sum();
+ graph.Values = Metrics.Ratings.Select(r => (float)r);
+ }
+ }
+
+ public UserRatings()
+ {
+ Children = new Drawable[]
+ {
+ header = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Text = "User Rating",
+ TextSize = 13,
+ },
+ ratingsBar = new Bar
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 5,
+ Margin = new MarginPadding { Top = 5 },
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new[]
+ {
+ negativeRatings = new OsuSpriteText
+ {
+ Text = "0",
+ TextSize = 13,
+ },
+ positiveRatings = new OsuSpriteText
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ Text = @"0",
+ TextSize = 13,
+ },
+ },
+ },
+ new OsuSpriteText
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Text = "Rating Spread",
+ TextSize = 13,
+ Margin = new MarginPadding { Top = 10, Bottom = 5 },
+ },
+ },
+ },
+ graphContainer = new Container
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ RelativeSizeAxes = Axes.Both,
+ Child = graph = new BarGraph
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ },
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ ratingsBar.BackgroundColour = colours.Green;
+ ratingsBar.AccentColour = colours.Yellow;
+ graph.Colour = colours.BlueDark;
+ }
+
+ protected override void UpdateAfterChildren()
+ {
+ base.UpdateAfterChildren();
+
+ graphContainer.Padding = new MarginPadding { Top = header.DrawHeight };
+ }
+ }
+}
diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
index 5a375e55d4..0506784614 100644
--- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
+++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
@@ -1,15 +1,15 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using System.Collections.Generic;
using OpenTK;
using OpenTK.Graphics;
+using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
-using System;
-using osu.Framework.Allocation;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
index d7c85fff90..4f81b2871d 100644
--- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
@@ -1,19 +1,22 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using OpenTK;
using OpenTK.Graphics;
+using osu.Framework;
+using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input;
using osu.Game.Graphics;
-using osu.Game.Graphics.Sprites;
-using osu.Framework.Extensions.Color4Extensions;
-using osu.Game.Rulesets.Mods;
-using osu.Game.Users;
-using osu.Framework;
-using osu.Game.Rulesets.Scoring;
using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Rulesets.UI;
+using osu.Game.Users;
namespace osu.Game.Screens.Select.Leaderboards
{
@@ -21,6 +24,8 @@ namespace osu.Game.Screens.Select.Leaderboards
{
public static readonly float HEIGHT = 60;
+ public event Action StateChanged;
+
public readonly int RankPosition;
public readonly Score Score;
@@ -38,14 +43,17 @@ namespace osu.Game.Screens.Select.Leaderboards
private readonly ScoreComponentLabel maxCombo;
private readonly ScoreComponentLabel accuracy;
private readonly Container flagBadgeContainer;
- private readonly FillFlowContainer modsContainer;
+ private readonly FillFlowContainer modsContainer;
private Visibility state;
+
public Visibility State
{
get { return state; }
set
{
+ if (state == value)
+ return;
state = value;
switch (state)
@@ -88,6 +96,8 @@ namespace osu.Game.Screens.Select.Leaderboards
break;
}
+
+ StateChanged?.Invoke(State);
}
}
@@ -239,7 +249,7 @@ namespace osu.Game.Screens.Select.Leaderboards
},
},
},
- modsContainer = new FillFlowContainer
+ modsContainer = new FillFlowContainer
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
@@ -252,13 +262,13 @@ namespace osu.Game.Screens.Select.Leaderboards
},
};
- if (Score.Mods != null)
+ foreach (Mod mod in Score.Mods)
{
- foreach (Mod mod in Score.Mods)
+ modsContainer.Add(new ModIcon(mod)
{
- // TODO: Get actual mod colours
- modsContainer.Add(new ScoreModIcon(mod.Icon, OsuColour.FromHex(@"ffcc22")));
- }
+ AutoSizeAxes = Axes.Both,
+ Scale = new Vector2(0.375f)
+ });
}
}
@@ -267,13 +277,13 @@ namespace osu.Game.Screens.Select.Leaderboards
public override void Hide() => State = Visibility.Hidden;
public override void Show() => State = Visibility.Visible;
- protected override bool OnHover(Framework.Input.InputState state)
+ protected override bool OnHover(InputState state)
{
background.FadeTo(0.5f, 300, Easing.OutQuint);
return base.OnHover(state);
}
- protected override void OnHoverLost(Framework.Input.InputState state)
+ protected override void OnHoverLost(InputState state)
{
background.FadeTo(background_alpha, 200, Easing.OutQuint);
base.OnHoverLost(state);
@@ -303,8 +313,8 @@ namespace osu.Game.Screens.Select.Leaderboards
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = font,
- TextSize = textSize,
FixedWidth = true,
+ TextSize = textSize,
Text = text,
Colour = glowColour,
Shadow = false,
@@ -326,36 +336,6 @@ namespace osu.Game.Screens.Select.Leaderboards
}
}
- private class ScoreModIcon : Container
- {
- public ScoreModIcon(FontAwesome icon, Color4 colour)
- {
- AutoSizeAxes = Axes.Both;
-
- Children = new[]
- {
- new SpriteIcon
- {
- Origin = Anchor.Centre,
- Anchor = Anchor.Centre,
- Icon = FontAwesome.fa_osu_mod_bg,
- Colour = colour,
- Shadow = true,
- Size = new Vector2(30),
- },
- new SpriteIcon
- {
- Origin = Anchor.Centre,
- Anchor = Anchor.Centre,
- Icon = icon,
- Colour = OsuColour.Gray(84),
- Size = new Vector2(18),
- Position = new Vector2(0f, 2f),
- },
- };
- }
- }
-
private class ScoreComponentLabel : Container
{
public ScoreComponentLabel(FontAwesome icon, string value)
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 3c522ca0e8..e0eafe8422 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -9,7 +9,7 @@
Properties
osu.Game
osu.Game
- v4.5
+ v4.6.1
512
@@ -98,6 +98,7 @@
+
@@ -133,6 +134,10 @@
+
+
+
+
@@ -533,6 +538,9 @@
+
+
+