diff --git a/NuGet.config b/NuGet.config
new file mode 100644
index 0000000000..95f993e510
--- /dev/null
+++ b/NuGet.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/appveyor.yml b/appveyor.yml
index b26a895788..21c15724e6 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,3 +1,4 @@
+# 2017-09-14
clone_depth: 1
version: '{branch}-{build}'
configuration: Debug
diff --git a/osu-framework b/osu-framework
index 5c22092e59..3f4545aae8 160000
--- a/osu-framework
+++ b/osu-framework
@@ -1 +1 @@
-Subproject commit 5c22092e590d589927962b8d0173dae5f9b1405c
+Subproject commit 3f4545aae82650dc87cac7dd5df64e6e47918da1
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/App.config b/osu.Desktop.Deploy/App.config
index 6711f9c54e..2fae7a5e1c 100644
--- a/osu.Desktop.Deploy/App.config
+++ b/osu.Desktop.Deploy/App.config
@@ -33,7 +33,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
-
+
diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs
index 37776b329f..a181a6fa5e 100644
--- a/osu.Desktop.Deploy/Program.cs
+++ b/osu.Desktop.Deploy/Program.cs
@@ -17,8 +17,8 @@ namespace osu.Desktop.Deploy
{
internal static class Program
{
- private const string nuget_path = @"packages\NuGet.CommandLine.3.5.0\tools\NuGet.exe";
- private const string squirrel_path = @"packages\squirrel.windows.1.5.2\tools\Squirrel.exe";
+ private const string nuget_path = @"packages\NuGet.CommandLine.4.3.0\tools\NuGet.exe";
+ private const string squirrel_path = @"packages\squirrel.windows.1.7.8\tools\Squirrel.exe";
private const string msbuild_path = @"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe";
public static string StagingFolder = ConfigurationManager.AppSettings["StagingFolder"];
@@ -100,7 +100,7 @@ namespace osu.Desktop.Deploy
updateAssemblyInfo(version);
write("Running build process...");
- runCommand(msbuild_path, $"/v:quiet /m /t:{TargetName.Replace('.', '_')} /p:OutputPath={stagingPath};Configuration=Release {SolutionName}.sln");
+ runCommand(msbuild_path, $"/v:quiet /m /t:{TargetName.Replace('.', '_')} /p:OutputPath={stagingPath};Targets=\"Clean;Build\";Configuration=Release {SolutionName}.sln");
write("Creating NuGet deployment package...");
runCommand(nuget_path, $"pack {NuSpecName} -Version {version} -Properties Configuration=Deploy -OutputDirectory {stagingPath} -BasePath {stagingPath}");
diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
index c6474eae5a..a66c9c8993 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
@@ -66,22 +66,23 @@
True
- $(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll
-
-
- $(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\NuGet.Squirrel.dll
+ $(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll
True
-
- $(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll
+
+ $(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\NuGet.Squirrel.dll
+ True
+
+
+ $(SolutionDir)\packages\SharpCompress.0.18.1\lib\net45\SharpCompress.dll
True
$(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll
True
-
- $(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\Squirrel.dll
+
+ $(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\Squirrel.dll
True
diff --git a/osu.Desktop.Deploy/packages.config b/osu.Desktop.Deploy/packages.config
index 3c5ca9f9a3..7725be5f5e 100644
--- a/osu.Desktop.Deploy/packages.config
+++ b/osu.Desktop.Deploy/packages.config
@@ -6,9 +6,9 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
-
-
-
+
+
+
-
+
\ No newline at end of file
diff --git a/osu.Desktop.Tests/OpenTK.dll.config b/osu.Desktop.Tests/OpenTK.dll.config
new file mode 100644
index 0000000000..5620e3d9e2
--- /dev/null
+++ b/osu.Desktop.Tests/OpenTK.dll.config
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/osu.Desktop.Tests/Visual/OsuTestCase.cs b/osu.Desktop.Tests/Visual/OsuTestCase.cs
index e54f7dbeb5..1b48ded4e2 100644
--- a/osu.Desktop.Tests/Visual/OsuTestCase.cs
+++ b/osu.Desktop.Tests/Visual/OsuTestCase.cs
@@ -1,20 +1,17 @@
// 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())
+ using (var host = new HeadlessGameHost(realtime: false))
host.Run(new OsuTestCaseTestRunner(this));
}
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/TestCaseContextMenu.cs b/osu.Desktop.Tests/Visual/TestCaseContextMenu.cs
index f0894f794a..0d9c5e7ed1 100644
--- a/osu.Desktop.Tests/Visual/TestCaseContextMenu.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseContextMenu.cs
@@ -69,28 +69,28 @@ namespace osu.Desktop.Tests.Visual
private class MyContextMenuContainer : Container, IHasContextMenu
{
- public ContextMenuItem[] ContextMenuItems => new ContextMenuItem[]
+ public MenuItem[] ContextMenuItems => new MenuItem[]
{
- new OsuContextMenuItem(@"Some option"),
- new OsuContextMenuItem(@"Highlighted option", MenuItemType.Highlighted),
- new OsuContextMenuItem(@"Another option"),
- new OsuContextMenuItem(@"Choose me please"),
- new OsuContextMenuItem(@"And me too"),
- new OsuContextMenuItem(@"Trying to fill"),
- new OsuContextMenuItem(@"Destructive option", MenuItemType.Destructive),
+ new OsuMenuItem(@"Some option"),
+ new OsuMenuItem(@"Highlighted option", MenuItemType.Highlighted),
+ new OsuMenuItem(@"Another option"),
+ new OsuMenuItem(@"Choose me please"),
+ new OsuMenuItem(@"And me too"),
+ new OsuMenuItem(@"Trying to fill"),
+ new OsuMenuItem(@"Destructive option", MenuItemType.Destructive),
};
}
private class AnotherContextMenuContainer : Container, IHasContextMenu
{
- public ContextMenuItem[] ContextMenuItems => new ContextMenuItem[]
+ public MenuItem[] ContextMenuItems => new MenuItem[]
{
- new OsuContextMenuItem(@"Simple option"),
- new OsuContextMenuItem(@"Simple very very long option"),
- new OsuContextMenuItem(@"Change width", MenuItemType.Highlighted) { Action = () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint) },
- new OsuContextMenuItem(@"Change height", MenuItemType.Highlighted) { Action = () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint) },
- new OsuContextMenuItem(@"Change width back", MenuItemType.Destructive) { Action = () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint) },
- new OsuContextMenuItem(@"Change height back", MenuItemType.Destructive) { Action = () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint) },
+ new OsuMenuItem(@"Simple option"),
+ new OsuMenuItem(@"Simple very very long option"),
+ new OsuMenuItem(@"Change width", MenuItemType.Highlighted, () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint)),
+ new OsuMenuItem(@"Change height", MenuItemType.Highlighted, () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint)),
+ new OsuMenuItem(@"Change width back", MenuItemType.Destructive, () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint)),
+ new OsuMenuItem(@"Change height back", MenuItemType.Destructive, () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint)),
};
}
}
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/TestCaseHitObjects.cs b/osu.Desktop.Tests/Visual/TestCaseHitObjects.cs
index 13e05d6477..cb365962a3 100644
--- a/osu.Desktop.Tests/Visual/TestCaseHitObjects.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseHitObjects.cs
@@ -6,7 +6,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using OpenTK;
@@ -110,10 +109,7 @@ namespace osu.Desktop.Tests.Visual
h.Depth = depth++;
if (auto)
- {
h.State = ArmedState.Hit;
- h.Judgement = new OsuJudgement { Result = HitResult.Hit };
- }
playfieldContainer.Add(h);
var proxyable = h as IDrawableHitObjectWithProxiedApproach;
diff --git a/osu.Desktop.Tests/Visual/TestCaseKeyCounter.cs b/osu.Desktop.Tests/Visual/TestCaseKeyCounter.cs
index 1861a77a3a..ffe37c83a6 100644
--- a/osu.Desktop.Tests/Visual/TestCaseKeyCounter.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseKeyCounter.cs
@@ -2,12 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.UserInterface;
using osu.Framework.MathUtils;
using osu.Game.Screens.Play;
-using OpenTK;
-using OpenTK.Graphics;
using OpenTK.Input;
namespace osu.Desktop.Tests.Visual
@@ -41,38 +37,5 @@ namespace osu.Desktop.Tests.Visual
Add(kc);
}
- private class TestSliderBar : SliderBar where T : struct
- {
- public Color4 Color
- {
- get { return Box.Colour; }
- set { Box.Colour = value; }
- }
-
- public Color4 SelectionColor
- {
- get { return SelectionBox.Colour; }
- set { SelectionBox.Colour = value; }
- }
-
- protected readonly Box SelectionBox;
- protected readonly Box Box;
-
- public TestSliderBar()
- {
- Children = new Drawable[]
- {
- Box = new Box { RelativeSizeAxes = Axes.Both },
- SelectionBox = new Box { RelativeSizeAxes = Axes.Both }
- };
- }
-
- protected override void UpdateValue(float value)
- {
- SelectionBox.ScaleTo(
- new Vector2(value, 1),
- 300, Easing.OutQuint);
- }
- }
}
}
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/TestCaseManiaHitObjects.cs b/osu.Desktop.Tests/Visual/TestCaseManiaHitObjects.cs
index 7dcc48c778..d855e86aa0 100644
--- a/osu.Desktop.Tests/Visual/TestCaseManiaHitObjects.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseManiaHitObjects.cs
@@ -3,6 +3,7 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using OpenTK;
@@ -40,8 +41,20 @@ namespace osu.Desktop.Tests.Visual
RelativeChildSize = new Vector2(1, 10000),
Children = new[]
{
- new DrawableNote(new Note { StartTime = 5000 }) { AccentColour = Color4.Red },
- new DrawableNote(new Note { StartTime = 6000 }) { AccentColour = Color4.Red }
+ new DrawableNote(new Note(), ManiaAction.Key1)
+ {
+ Y = 5000,
+ LifetimeStart = double.MinValue,
+ LifetimeEnd = double.MaxValue,
+ AccentColour = Color4.Red
+ },
+ new DrawableNote(new Note(), ManiaAction.Key1)
+ {
+ Y = 6000,
+ LifetimeStart = double.MinValue,
+ LifetimeEnd = double.MaxValue,
+ AccentColour = Color4.Red
+ }
}
}
}
@@ -62,11 +75,14 @@ namespace osu.Desktop.Tests.Visual
RelativeChildSize = new Vector2(1, 10000),
Children = new[]
{
- new DrawableHoldNote(new HoldNote
+ new DrawableHoldNote(new HoldNote(), ManiaAction.Key1)
{
- StartTime = 5000,
- Duration = 1000
- }) { AccentColour = Color4.Red }
+ Y = 5000,
+ Height = 1000,
+ LifetimeStart = double.MinValue,
+ LifetimeEnd = double.MaxValue,
+ AccentColour = Color4.Red
+ }
}
}
}
diff --git a/osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs b/osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs
index ed0e5d81e9..28cc7e5af8 100644
--- a/osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs
@@ -3,108 +3,36 @@
using System;
using System.Linq;
-using osu.Framework.Configuration;
+using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
-using osu.Framework.Input;
using osu.Framework.Timing;
+using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Timing;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Timing;
-using OpenTK;
-using OpenTK.Input;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mania.Judgements;
+using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Desktop.Tests.Visual
{
internal class TestCaseManiaPlayfield : OsuTestCase
{
+ private const double start_time = 500;
+ private const double duration = 500;
+
public override string Description => @"Mania playfield";
protected override double TimePerAction => 200;
+ private RulesetInfo maniaRuleset;
+
public TestCaseManiaPlayfield()
{
- Action createPlayfield = (cols, pos) =>
- {
- Clear();
- Add(new ManiaPlayfield(cols)
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- SpecialColumnPosition = pos,
- Scale = new Vector2(1, -1)
- });
- };
-
- const double start_time = 500;
- const double duration = 500;
-
- Func createTimingChange = (time, gravity) => new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(time)
- {
- TimingPoint = { BeatLength = 1000 }
- }, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic);
-
- Action createPlayfieldWithNotes = gravity =>
- {
- Clear();
-
- var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
-
- ManiaPlayfield playField;
- Add(playField = new ManiaPlayfield(4)
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Scale = new Vector2(1, -1),
- Clock = new FramedClock(rateAdjustClock)
- });
-
- if (!gravity)
- playField.Columns.ForEach(c => c.Add(createTimingChange(0, false)));
-
- for (double t = start_time; t <= start_time + duration; t += 100)
- {
- if (gravity)
- playField.Columns.ElementAt(0).Add(createTimingChange(t, true));
-
- playField.Add(new DrawableNote(new Note
- {
- StartTime = t,
- Column = 0
- }, new Bindable(Key.D)));
-
- if (gravity)
- playField.Columns.ElementAt(3).Add(createTimingChange(t, true));
-
- playField.Add(new DrawableNote(new Note
- {
- StartTime = t,
- Column = 3
- }, new Bindable(Key.K)));
- }
-
- if (gravity)
- playField.Columns.ElementAt(1).Add(createTimingChange(start_time, true));
-
- playField.Add(new DrawableHoldNote(new HoldNote
- {
- StartTime = start_time,
- Duration = duration,
- Column = 1
- }, new Bindable(Key.F)));
-
- if (gravity)
- playField.Columns.ElementAt(2).Add(createTimingChange(start_time, true));
-
- playField.Add(new DrawableHoldNote(new HoldNote
- {
- StartTime = start_time,
- Duration = duration,
- Column = 2
- }, new Bindable(Key.J)));
- };
+ var rng = new Random(1337);
AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal));
AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal));
@@ -114,29 +42,121 @@ namespace osu.Desktop.Tests.Visual
AddStep("8 columns", () => createPlayfield(8, SpecialColumnPosition.Normal));
AddStep("Left special style", () => createPlayfield(8, SpecialColumnPosition.Left));
AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right));
+ AddStep("Reversed", () => createPlayfield(4, SpecialColumnPosition.Normal, true));
AddStep("Notes with input", () => createPlayfieldWithNotes(false));
- AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction));
-
+ AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(false, true));
AddStep("Notes with gravity", () => createPlayfieldWithNotes(true));
- AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction));
- }
+ AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true, true));
- private void triggerKeyDown(Column column)
- {
- column.TriggerOnKeyDown(new InputState(), new KeyDownEventArgs
+ AddStep("Hit explosion", () =>
{
- Key = column.Key,
- Repeat = false
+ var playfield = createPlayfield(4, SpecialColumnPosition.Normal);
+
+ int col = rng.Next(0, 4);
+
+ var note = new DrawableNote(new Note { Column = col }, ManiaAction.Key1)
+ {
+ AccentColour = playfield.Columns.ElementAt(col).AccentColour
+ };
+
+ playfield.OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect });
});
}
- private void triggerKeyUp(Column column)
+ [BackgroundDependencyLoader]
+ private void load(RulesetStore rulesets)
{
- column.TriggerOnKeyUp(new InputState(), new KeyUpEventArgs
+ maniaRuleset = rulesets.GetRuleset(3);
+ }
+
+ private SpeedAdjustmentContainer createTimingChange(double time, bool gravity) => new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(time)
+ {
+ TimingPoint = { BeatLength = 1000 }
+ }, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic);
+
+ private ManiaPlayfield createPlayfield(int cols, SpecialColumnPosition specialPos, bool inverted = false)
+ {
+ Clear();
+
+ var inputManager = new ManiaInputManager(maniaRuleset, cols) { RelativeSizeAxes = Axes.Both };
+ Add(inputManager);
+
+ ManiaPlayfield playfield;
+ inputManager.Add(playfield = new ManiaPlayfield(cols)
{
- Key = column.Key
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ SpecialColumnPosition = specialPos
});
+
+ playfield.Inverted.Value = inverted;
+
+ return playfield;
+ }
+
+ private void createPlayfieldWithNotes(bool gravity, bool inverted = false)
+ {
+ Clear();
+
+ var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
+
+ var inputManager = new ManiaInputManager(maniaRuleset, 4) { RelativeSizeAxes = Axes.Both };
+ Add(inputManager);
+
+ ManiaPlayfield playfield;
+ inputManager.Add(playfield = new ManiaPlayfield(4)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Clock = new FramedClock(rateAdjustClock)
+ });
+
+ playfield.Inverted.Value = inverted;
+
+ if (!gravity)
+ playfield.Columns.ForEach(c => c.Add(createTimingChange(0, false)));
+
+ for (double t = start_time; t <= start_time + duration; t += 100)
+ {
+ if (gravity)
+ playfield.Columns.ElementAt(0).Add(createTimingChange(t, true));
+
+ playfield.Add(new DrawableNote(new Note
+ {
+ StartTime = t,
+ Column = 0
+ }, ManiaAction.Key1));
+
+ if (gravity)
+ playfield.Columns.ElementAt(3).Add(createTimingChange(t, true));
+
+ playfield.Add(new DrawableNote(new Note
+ {
+ StartTime = t,
+ Column = 3
+ }, ManiaAction.Key4));
+ }
+
+ if (gravity)
+ playfield.Columns.ElementAt(1).Add(createTimingChange(start_time, true));
+
+ playfield.Add(new DrawableHoldNote(new HoldNote
+ {
+ StartTime = start_time,
+ Duration = duration,
+ Column = 1
+ }, ManiaAction.Key2));
+
+ if (gravity)
+ playfield.Columns.ElementAt(2).Add(createTimingChange(start_time, true));
+
+ playfield.Add(new DrawableHoldNote(new HoldNote
+ {
+ StartTime = start_time,
+ Duration = duration,
+ Column = 2
+ }, ManiaAction.Key3));
}
}
}
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/Visual/TestCasePlayer.cs b/osu.Desktop.Tests/Visual/TestCasePlayer.cs
index f5d02d3f2b..03ee82dc73 100644
--- a/osu.Desktop.Tests/Visual/TestCasePlayer.cs
+++ b/osu.Desktop.Tests/Visual/TestCasePlayer.cs
@@ -1,18 +1,18 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System.Collections.Generic;
using osu.Desktop.Tests.Beatmaps;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
-using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Rulesets.Osu.UI;
using osu.Game.Screens.Play;
-using OpenTK;
using OpenTK.Graphics;
+using osu.Game.Rulesets.Mods;
+using System.Linq;
+using osu.Game.Beatmaps.Formats;
+using System.Text;
+using System.IO;
namespace osu.Desktop.Tests.Visual
{
@@ -33,55 +33,739 @@ namespace osu.Desktop.Tests.Visual
{
base.LoadComplete();
- var objects = new List();
-
- int time = 1500;
- for (int i = 0; i < 50; i++)
- {
- objects.Add(new HitCircle
- {
- StartTime = time,
- Position = new Vector2(i % 4 == 0 || i % 4 == 2 ? 0 : OsuPlayfield.BASE_SIZE.X,
- i % 4 < 2 ? 0 : OsuPlayfield.BASE_SIZE.Y),
- NewCombo = i % 4 == 0
- });
-
- time += 500;
- }
-
- Beatmap b = new Beatmap
- {
- HitObjects = objects,
- BeatmapInfo = new BeatmapInfo
- {
- Difficulty = new BeatmapDifficulty(),
- Ruleset = rulesets.Query().First(),
- Metadata = new BeatmapMetadata
- {
- Artist = @"Unknown",
- Title = @"Sample Beatmap",
- Author = @"peppy",
- }
- }
- };
-
- WorkingBeatmap beatmap = new TestWorkingBeatmap(b);
-
Add(new Box
{
RelativeSizeAxes = Framework.Graphics.Axes.Both,
Colour = Color4.Black,
});
- Add(Player = CreatePlayer(beatmap));
+ foreach (var r in rulesets.Query())
+ AddStep(r.Name, () => loadPlayerFor(r));
+
+ loadPlayerFor(rulesets.Query().First());
}
- protected virtual Player CreatePlayer(WorkingBeatmap beatmap)
+ private void loadPlayerFor(RulesetInfo r)
+ {
+ Beatmap beatmap;
+
+ using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data)))
+ using (var reader = new StreamReader(stream))
+ beatmap = BeatmapDecoder.GetDecoder(reader).Decode(reader);
+
+ beatmap.BeatmapInfo.Ruleset = r;
+
+ var instance = r.CreateInstance();
+
+ WorkingBeatmap working = new TestWorkingBeatmap(beatmap);
+ working.Mods.Value = new[] { instance.GetAllMods().First(m => m is ModNoFail) };
+
+ if (Player != null)
+ Remove(Player);
+
+ Add(Player = CreatePlayer(working, instance));
+ }
+
+ protected virtual Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
{
return new Player
{
InitialBeatmap = beatmap
};
}
+
+ private const string test_beatmap_data =
+@"osu file format v14
+
+[General]
+AudioLeadIn: 500
+PreviewTime: 53498
+Countdown: 0
+SampleSet: Soft
+StackLeniency: 0.7
+Mode: 0
+LetterboxInBreaks: 0
+WidescreenStoryboard: 1
+
+[Editor]
+DistanceSpacing: 1.2
+BeatDivisor: 4
+GridSize: 4
+TimelineZoom: 1
+
+[Metadata]
+Title:My Love
+TitleUnicode:My Love
+Artist:Kuba Oms
+ArtistUnicode:Kuba Oms
+Creator:W h i t e
+Version:Hard
+Source:ADHD
+Tags:Monthly Beatmapping Contest Electronic folk pop w_h_i_t_e
+BeatmapID:397534
+BeatmapSetID:163112
+
+[Difficulty]
+HPDrainRate:5
+CircleSize:4
+OverallDifficulty:6
+ApproachRate:7
+SliderMultiplier:1.44
+SliderTickRate:2
+
+[Events]
+//Break Periods
+2,69870,83770
+2,152170,158770
+//Storyboard Layer 0 (Background)
+//Storyboard Layer 1 (Fail)
+//Storyboard Layer 2 (Pass)
+//Storyboard Layer 3 (Foreground)
+//Storyboard Sound Samples
+
+[TimingPoints]
+2170,468.75,4,2,0,40,1,0
+4045,-100,4,2,0,30,0,0
+4162,-100,4,2,0,40,0,0
+5920,-100,4,2,0,30,0,0
+6037,-100,4,2,0,40,0,0
+7795,-100,4,2,0,30,0,0
+7912,-100,4,2,0,40,0,0
+9670,-100,4,2,0,40,0,0
+9787,-100,4,2,0,50,0,0
+11545,-100,4,2,0,40,0,0
+11662,-100,4,2,0,50,0,0
+13420,-100,4,2,0,40,0,0
+13537,-100,4,2,0,50,0,0
+15295,-100,4,2,0,40,0,0
+15412,-100,4,2,0,50,0,0
+17170,-100,4,2,0,40,0,0
+17287,-100,4,2,0,50,0,0
+19045,-100,4,2,0,40,0,0
+19162,-100,4,2,0,50,0,0
+20920,-100,4,2,0,40,0,0
+21037,-100,4,2,0,50,0,0
+22795,-100,4,2,0,40,0,0
+22912,-100,4,2,0,50,0,0
+24670,-100,4,2,0,70,0,0
+37560,-200,4,2,0,30,0,0
+38263,-200,4,2,0,5,0,0
+38966,-100,4,2,0,30,0,0
+39670,-100,4,2,0,70,0,0
+53732,-100,4,2,0,40,0,0
+54670,-100,4,2,0,80,0,1
+55138,-100,4,2,0,60,0,1
+55255,-100,4,2,0,80,0,1
+56076,-100,4,2,0,60,0,1
+56193,-100,4,2,0,80,0,1
+57013,-100,4,2,0,60,0,1
+57130,-100,4,2,0,80,0,1
+57951,-100,4,2,0,60,0,1
+58068,-100,4,2,0,80,0,1
+58888,-100,4,2,0,60,0,1
+59005,-100,4,2,0,80,0,1
+59826,-100,4,2,0,60,0,1
+59943,-100,4,2,0,80,0,1
+60763,-100,4,2,0,60,0,1
+60880,-100,4,2,0,80,0,1
+61701,-100,4,2,0,60,0,1
+61818,-100,4,2,0,80,0,1
+62638,-100,4,2,0,60,0,1
+62755,-100,4,2,0,80,0,1
+63576,-100,4,2,0,60,0,1
+63693,-100,4,2,0,80,0,1
+64513,-100,4,2,0,60,0,1
+64630,-100,4,2,0,80,0,1
+65451,-100,4,2,0,60,0,1
+65568,-100,4,2,0,80,0,1
+66388,-100,4,2,0,60,0,1
+66505,-100,4,2,0,80,0,1
+67326,-100,4,2,0,60,0,1
+67443,-100,4,2,0,80,0,1
+68263,-100,4,2,0,60,0,1
+68380,-100,4,2,0,80,0,1
+69201,-100,4,2,0,60,0,1
+69318,-100,4,2,0,80,0,1
+69670,-100,4,2,0,70,0,0
+84670,-100,4,2,0,70,0,0
+97560,-200,4,2,0,70,0,0
+97795,-200,4,2,0,30,0,0
+98966,-100,4,2,0,30,0,0
+99670,-100,4,2,0,70,0,0
+113732,-100,4,2,0,40,0,0
+114670,-100,4,2,0,80,0,1
+115138,-100,4,2,0,60,0,1
+115255,-100,4,2,0,80,0,1
+116076,-100,4,2,0,60,0,1
+116193,-100,4,2,0,80,0,1
+117013,-100,4,2,0,60,0,1
+117130,-100,4,2,0,80,0,1
+117951,-100,4,2,0,60,0,1
+118068,-100,4,2,0,80,0,1
+118888,-100,4,2,0,60,0,1
+119005,-100,4,2,0,80,0,1
+119826,-100,4,2,0,60,0,1
+119943,-100,4,2,0,80,0,1
+120763,-100,4,2,0,60,0,1
+120880,-100,4,2,0,80,0,1
+121701,-100,4,2,0,60,0,1
+121818,-100,4,2,0,80,0,1
+122638,-100,4,2,0,60,0,1
+122755,-100,4,2,0,80,0,1
+123576,-100,4,2,0,60,0,1
+123693,-100,4,2,0,80,0,1
+124513,-100,4,2,0,60,0,1
+124630,-100,4,2,0,80,0,1
+125451,-100,4,2,0,60,0,1
+125568,-100,4,2,0,80,0,1
+126388,-100,4,2,0,60,0,1
+126505,-100,4,2,0,80,0,1
+127326,-100,4,2,0,60,0,1
+127443,-100,4,2,0,80,0,1
+128263,-100,4,2,0,60,0,1
+128380,-100,4,2,0,80,0,1
+129201,-100,4,2,0,60,0,1
+129318,-100,4,2,0,80,0,1
+129670,-200,4,2,0,40,0,0
+144670,-133.333333333333,4,2,0,40,0,0
+159670,-133.333333333333,4,2,0,40,0,0
+163420,-133.333333333333,4,2,0,45,0,0
+163888,-125,4,2,0,50,0,0
+164357,-117.647058823529,4,2,0,55,0,0
+164826,-111.111111111111,4,2,0,60,0,0
+165295,-105.263157894737,4,2,0,65,0,0
+165763,-100,4,2,0,70,0,0
+166232,-100,4,2,0,40,0,0
+167170,-100,4,2,0,80,0,1
+167638,-100,4,2,0,60,0,1
+167755,-100,4,2,0,80,0,1
+168576,-100,4,2,0,60,0,1
+168693,-100,4,2,0,80,0,1
+169513,-100,4,2,0,60,0,1
+169630,-100,4,2,0,80,0,1
+170451,-100,4,2,0,60,0,1
+170568,-100,4,2,0,80,0,1
+171388,-100,4,2,0,60,0,1
+171505,-100,4,2,0,80,0,1
+172326,-100,4,2,0,60,0,1
+172443,-100,4,2,0,80,0,1
+173263,-100,4,2,0,60,0,1
+173380,-100,4,2,0,80,0,1
+174201,-100,4,2,0,60,0,1
+174318,-100,4,2,0,80,0,1
+175138,-100,4,2,0,60,0,1
+175255,-100,4,2,0,80,0,1
+176076,-100,4,2,0,60,0,1
+176193,-100,4,2,0,80,0,1
+177013,-100,4,2,0,60,0,1
+177130,-100,4,2,0,80,0,1
+177951,-100,4,2,0,60,0,1
+178068,-100,4,2,0,80,0,1
+178888,-100,4,2,0,60,0,1
+179005,-100,4,2,0,80,0,1
+179826,-100,4,2,0,60,0,1
+179943,-100,4,2,0,80,0,1
+180763,-100,4,2,0,60,0,1
+180880,-100,4,2,0,80,0,1
+180998,-100,4,2,0,80,0,0
+181466,-100,4,2,0,60,0,0
+181584,-100,4,2,0,80,0,0
+181935,-100,4,2,0,80,0,0
+182170,-100,4,2,0,80,0,1
+182638,-100,4,2,0,60,0,1
+182755,-100,4,2,0,80,0,1
+183576,-100,4,2,0,60,0,1
+183693,-100,4,2,0,80,0,1
+184513,-100,4,2,0,60,0,1
+184630,-100,4,2,0,80,0,1
+185451,-100,4,2,0,60,0,1
+185568,-100,4,2,0,80,0,1
+186388,-100,4,2,0,60,0,1
+186505,-100,4,2,0,80,0,1
+187326,-100,4,2,0,60,0,1
+187443,-100,4,2,0,80,0,1
+188263,-100,4,2,0,60,0,1
+188380,-100,4,2,0,80,0,1
+189201,-100,4,2,0,60,0,1
+189318,-100,4,2,0,80,0,1
+190138,-100,4,2,0,60,0,1
+190255,-100,4,2,0,80,0,1
+191076,-100,4,2,0,60,0,1
+191193,-100,4,2,0,80,0,1
+192013,-100,4,2,0,60,0,1
+192130,-100,4,2,0,80,0,1
+192951,-100,4,2,0,60,0,1
+193068,-100,4,2,0,80,0,1
+193888,-100,4,2,0,60,0,1
+194005,-100,4,2,0,80,0,1
+194826,-100,4,2,0,60,0,1
+194943,-100,4,2,0,80,0,1
+195295,-100,4,2,0,50,0,1
+195529,-100,4,2,0,52,0,1
+195646,-100,4,2,0,54,0,1
+195763,-100,4,2,0,56,0,1
+195880,-100,4,2,0,58,0,1
+195998,-100,4,2,0,60,0,1
+196115,-100,4,2,0,62,0,1
+196232,-100,4,2,0,64,0,1
+196349,-100,4,2,0,68,0,1
+196466,-100,4,2,0,70,0,1
+196584,-100,4,2,0,72,0,1
+196701,-100,4,2,0,74,0,1
+196818,-100,4,2,0,76,0,1
+196935,-100,4,2,0,78,0,1
+197052,-100,4,2,0,80,0,1
+197170,-100,4,2,0,80,0,0
+197873,-100,4,2,0,60,0,0
+197990,-100,4,2,0,80,0,0
+198341,-100,4,2,0,60,0,0
+199045,-100,4,2,0,80,0,0
+199279,-100,4,2,0,60,0,0
+199630,-100,4,2,0,80,0,0
+200216,-100,4,2,0,60,0,0
+200334,-100,4,2,0,80,0,0
+201623,-100,4,2,0,60,0,0
+201740,-100,4,2,0,80,0,0
+202326,-100,4,2,0,60,0,0
+202443,-100,4,2,0,80,0,0
+203029,-100,4,2,0,60,0,0
+203498,-100,4,2,0,80,0,0
+203966,-100,4,2,0,60,0,0
+204201,-100,4,2,0,80,0,0
+205373,-100,4,2,0,60,0,0
+205490,-100,4,2,0,80,0,0
+205841,-100,4,2,0,60,0,0
+206076,-100,4,2,0,60,0,0
+206545,-100,4,2,0,80,0,0
+206779,-100,4,2,0,60,0,0
+207130,-100,4,2,0,80,0,0
+207716,-100,4,2,0,60,0,0
+207951,-100,4,2,0,80,0,0
+209123,-100,4,2,0,60,0,0
+209240,-100,4,2,0,80,0,0
+209826,-100,4,2,0,60,0,0
+209943,-100,4,2,0,80,0,0
+210529,-100,4,2,0,60,0,0
+210880,-100,4,2,0,80,0,0
+211232,-100,4,2,0,60,0,0
+211701,-100,4,2,0,70,0,0
+212170,-100,4,2,0,80,0,0
+212873,-100,4,2,0,60,0,0
+212990,-100,4,2,0,80,0,0
+213341,-100,4,2,0,60,0,0
+213576,-100,4,2,0,60,0,0
+214045,-100,4,2,0,80,0,0
+214279,-100,4,2,0,60,0,0
+214630,-100,4,2,0,80,0,0
+215216,-100,4,2,0,60,0,0
+215451,-100,4,2,0,80,0,0
+216623,-100,4,2,0,60,0,0
+216740,-100,4,2,0,80,0,0
+217326,-100,4,2,0,60,0,0
+217443,-100,4,2,0,80,0,0
+218029,-100,4,2,0,60,0,0
+218498,-100,4,2,0,80,0,0
+218732,-100,4,2,0,50,0,0
+219670,-100,4,2,0,70,0,0
+220138,-100,4,2,0,65,0,0
+220373,-100,4,2,0,45,0,0
+220490,-100,4,2,0,65,0,0
+220607,-100,4,2,0,60,0,0
+220841,-100,4,2,0,35,0,0
+221076,-100,4,2,0,35,0,0
+221545,-100,4,2,0,50,0,0
+221779,-100,4,2,0,30,0,0
+222013,-111.111111111111,4,2,0,25,0,0
+222130,-111.111111111111,4,2,0,40,0,0
+222482,-125,4,2,0,40,0,0
+222716,-125,4,2,0,20,0,0
+222951,-100,4,2,0,15,0,0
+223420,-100,4,2,0,30,0,0
+224357,-100,4,2,0,25,0,0
+225295,-100,4,2,0,20,0,0
+226232,-100,4,2,0,15,0,0
+226701,-100,4,2,0,10,0,0
+227170,-100,4,2,0,5,0,0
+
+
+[Colours]
+ Combo1 : 17,254,176
+Combo2 : 173,255,95
+Combo3 : 255,88,100
+Combo4 : 255,94,55
+
+[HitObjects]
+320,256,2170,6,0,P|256:284|192:256,1,144,4|0,0:0|0:0,0:0:0:0:
+144,184,2873,1,0,0:0:0:0:
+108,260,3107,2,0,P|112:296|100:336,1,72
+28,288,3576,2,0,P|24:252|36:212,1,72,0|0,0:0|0:0,0:0:0:0:
+76,140,4045,6,0,L|220:136,1,144,4|0,0:0|0:0,0:0:0:0:
+292,88,4748,1,0,0:0:0:0:
+292,88,4982,2,0,P|304:120|300:168,1,72
+388,168,5451,2,0,P|396:133|416:103,1,72,0|0,0:0|0:0,0:0:0:0:
+472,172,5920,6,0,B|470:200|457:222|457:222|488:256|476:308,1,144,4|0,0:0|0:0,0:0:0:0:
+396,280,6623,1,0,0:0:0:0:
+324,328,6857,2,0,P|288:332|252:324,1,72
+180,280,7326,2,0,L|108:284,1,72,0|0,0:0|0:0,0:0:0:0:
+256,192,7795,12,0,9670,0:0:0:0:
+428,212,10138,1,0,0:0:0:0:
+292,320,10607,1,0,0:0:0:0:
+184,184,11076,2,0,L|112:180,1,72,0|0,0:0|0:0,0:0:0:0:
+24,172,11545,5,6,0:0:0:0:
+160,280,12013,1,0,0:0:0:0:
+268,144,12482,1,0,0:0:0:0:
+132,36,12951,2,0,L|204:32,1,72,0|0,0:0|0:0,0:0:0:0:
+284,60,13420,6,0,P|340:100|344:180,2,144,6|0|0,0:0|0:0|0:0,0:0:0:0:
+268,144,14591,1,0,0:0:0:0:
+284,228,14826,2,0,P|316:248|364:252,1,72,0|0,0:0|0:0,0:0:0:0:
+436,248,15295,6,0,P|372:272|344:340,1,144,6|2,0:0|0:0,0:0:0:0:
+168,338,16232,2,0,P|141:273|76:248,1,144,2|2,0:0|0:0,0:0:0:0:
+4,296,16935,1,0,0:0:0:0:
+80,336,17170,5,6,0:0:0:0:
+44,168,17638,1,0,0:0:0:0:
+212,128,18107,1,0,0:0:0:0:
+248,296,18576,2,0,P|284:288|320:292,1,72,0|0,0:0|0:0,0:0:0:0:
+400,324,19045,5,6,0:0:0:0:
+280,200,19513,1,0,0:0:0:0:
+368,52,19982,1,0,0:0:0:0:
+488,176,20451,2,0,P|452:168|416:172,1,72,0|0,0:0|0:0,0:0:0:0:
+336,200,20920,6,0,P|284:216|200:192,1,144,6|0,0:0|0:0,0:0:0:0:
+200,192,21857,2,0,L|204:264,1,72,0|0,0:3|0:0,0:0:0:0:
+117,244,22326,2,0,L|120:172,1,72,0|0,0:0|0:0,0:0:0:0:
+40,152,22795,6,0,L|28:296,2,144,6|0|0,0:0|0:0|0:0,0:0:0:0:
+152,24,24201,1,0,0:0:0:0:
+220,76,24435,1,0,3:0:0:0:
+304,56,24670,6,0,P|288:120|296:196,1,144,4|2,0:3|0:3,0:0:0:0:
+344,268,25373,1,0,0:0:0:0:
+416,316,25607,2,0,P|452:312|508:316,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
+244,344,26545,6,0,P|176:356|108:328,1,144,4|2,0:3|0:3,0:0:0:0:
+60,256,27248,1,0,0:0:0:0:
+36,172,27482,2,0,L|40:100,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
+188,252,28420,6,0,P|192:184|196:100,1,144,4|2,0:3|0:3,0:0:0:0:
+140,40,29123,1,0,0:0:0:0:
+140,40,29357,2,0,B|172:16|220:24|220:24|288:36,1,144,0|2,0:0|0:3,0:0:0:0:
+364,52,30060,1,0,0:0:0:0:
+308,116,30295,6,0,B|300:168|300:168|328:256,1,144,4|2,0:3|0:3,0:0:0:0:
+340,340,30998,1,0,0:0:0:0:
+260,308,31232,2,0,L|188:304,1,72,0|2,0:0|0:3,0:0:0:0:
+100,296,31701,1,2,0:3:0:0:
+136,374,31935,1,0,0:0:0:0:
+152,224,32170,6,0,P|160:152|132:88,1,144,4|2,0:3|0:3,0:0:0:0:
+56,48,32873,1,0,0:0:0:0:
+60,136,33107,2,0,L|56:208,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
+224,76,34045,6,0,P|289:104|360:96,1,144,4|2,0:3|0:3,0:0:0:0:
+432,48,34748,1,0,0:0:0:0:
+440,132,34982,2,0,B|432:156|432:156|436:204,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
+448,304,35920,6,0,B|412:315|380:292|380:292|348:269|312:280,1,144,4|2,0:3|0:3,0:0:0:0:
+332,364,36623,1,0,0:0:0:0:
+247,339,36857,2,0,P|230:308|225:273,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
+312,280,37560,6,0,L|316:172,1,108
+134,35,38966,5,0,0:0:0:0:
+72,96,39201,2,0,P|119:119|171:111,1,108,0|0,0:0|0:0,0:0:0:0:
+192,100,39670,6,0,L|200:172,1,72,4|2,0:0|0:0,0:0:0:0:
+147,240,40138,2,0,P|133:272|132:308,1,72,0|2,1:0|0:0,0:0:0:0:
+216,292,40607,2,0,B|260:308|260:308|356:292,1,144,4|0,2:3|1:0,1:0:0:0:
+356,292,41310,1,2,0:0:0:0:
+436,327,41545,6,0,P|441:292|435:257,1,72,4|2,0:3|0:0,0:0:0:0:
+364,204,42013,2,0,P|336:144|352:68,1,144,0|4,1:0|2:3,1:0:0:0:
+404,0,42716,1,2,0:0:0:0:
+440,80,42951,2,0,B|464:84|464:84|512:80,1,72,0|2,1:0|0:0,0:0:0:0:
+351,71,43420,6,0,B|296:68|296:68|268:76|268:76|196:72,1,144,4|0,2:3|1:0,1:0:0:0:
+120,68,44123,1,2,0:0:0:0:
+160,144,44357,2,0,P|172:180|168:232,1,72,4|2,0:3|0:0,0:0:0:0:
+76,264,44826,2,0,P|76:228|88:194,1,72,0|2,1:0|0:0,0:0:0:0:
+160,144,45295,5,4,0:3:0:0:
+244,164,45529,1,2,0:0:0:0:
+268,248,45763,2,0,L|344:252,1,72,0|2,1:0|0:0,0:0:0:0:
+408,156,46232,2,0,L|336:159,1,72,4|2,0:3|0:0,0:0:0:0:
+212,72,46701,2,0,L|288:76,1,72,0|2,1:0|0:0,0:0:0:0:
+400,72,47170,6,0,P|464:96|488:172,1,144,4|0,2:0|1:0,1:0:0:0:
+476,248,47873,1,2,0:0:0:0:
+436,324,48107,2,0,L|284:320,1,144,4|0,2:3|1:0,1:0:0:0:
+204,316,48810,1,2,0:0:0:0:
+127,355,49045,6,0,P|120:321|124:285,1,72,4|2,0:3|0:0,0:0:0:0:
+192,232,49513,2,0,L|335:228,1,144,0|4,1:0|2:3,1:0:0:0:
+412,188,50216,1,2,0:0:0:0:
+444,108,50451,2,0,P|452:72|448:36,1,72,0|2,1:0|0:0,0:0:0:0:
+368,68,50920,6,0,B|332:79|300:56|300:56|268:33|232:44,1,144,4|0,2:3|1:0,1:0:0:0:
+152,76,51623,1,2,0:0:0:0:
+76,116,51857,2,0,L|80:268,1,144,4|0,2:3|1:0,1:0:0:0:
+80,260,52560,1,2,0:0:0:0:
+8,308,52795,6,0,P|34:334|69:346,1,72,4|2,0:3|0:0,0:0:0:0:
+148,312,53263,2,0,P|163:278|162:241,1,72,0|2,1:0|0:0,0:0:0:0:
+156,156,53732,5,0,3:0:0:0:
+156,156,53966,1,2,0:0:0:0:
+236,196,54201,2,0,L|312:192,1,72,8|0,0:3|0:0,0:0:0:0:
+368,256,54670,6,0,P|392:216|352:116,1,144,4|2,0:0|1:2,0:0:0:0:
+288,92,55373,1,0,0:0:0:0:
+360,40,55607,2,0,L|432:36,1,72,4|0,0:3|3:0,0:0:0:0:
+288,92,56076,2,0,L|216:88,1,72,2|0,1:2|0:0,0:0:0:0:
+132,72,56545,6,0,P|172:88|200:184,1,144,4|2,0:3|1:2,0:0:0:0:
+143,241,57248,1,0,0:0:0:0:
+65,202,57482,2,0,P|87:174|119:157,1,72,4|0,0:3|3:0,0:0:0:0:
+132,324,57951,2,0,P|98:312|72:288,1,72,2|0,1:2|0:0,0:0:0:0:
+143,241,58420,6,0,L|288:240,1,144,4|2,0:3|1:2,0:0:0:0:
+372,240,59123,1,0,0:0:0:0:
+330,314,59357,2,0,P|318:350|322:390,1,72,4|0,0:3|3:0,0:0:0:0:
+452,264,59826,2,0,P|453:228|442:194,1,72,2|0,1:2|0:0,0:0:0:0:
+384,128,60295,6,0,B|336:144|336:144|244:128,1,144,4|2,0:3|1:2,0:0:0:0:
+164,160,60998,2,0,P|160:116|168:88,1,72,0|4,0:0|0:3,0:0:0:0:
+244,128,61466,2,0,P|248:172|240:200,1,72,0|2,3:0|1:2,0:0:0:0:
+168,248,61935,1,0,0:0:0:0:
+120,320,62170,6,0,P|196:328|252:272,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0:
+80,244,63341,1,0,3:0:0:0:
+100,160,63576,2,0,L|24:156,1,72,2|0,1:2|0:0,0:0:0:0:
+180,128,64045,6,0,P|249:138|304:94,1,144,4|2,0:3|1:2,0:0:0:0:
+226,57,64748,1,0,0:0:0:0:
+304,94,64982,2,0,L|300:166,1,72,4|0,0:3|3:0,0:0:0:0:
+377,203,65451,2,0,L|388:132,1,72,2|0,1:2|0:0,0:0:0:0:
+468,180,65920,6,0,L|432:328,1,144,4|2,0:3|1:2,0:0:0:0:
+276,252,66857,2,0,P|208:248|140:280,1,144,4|2,0:3|1:2,0:0:0:0:
+84,344,67560,1,0,0:0:0:0:
+56,260,67795,6,0,L|52:188,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
+168,128,68732,2,0,L|172:56,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
+244,168,69435,1,0,0:0:0:0:
+332,164,69670,1,4,0:3:0:0:
+208,328,84670,6,0,P|224:264|216:188,1,144,4|2,0:3|0:3,0:0:0:0:
+168,116,85373,1,0,0:0:0:0:
+96,68,85607,2,0,P|60:72|4:68,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
+268,40,86545,6,0,P|336:28|404:56,1,144,4|2,0:3|0:3,0:0:0:0:
+452,128,87248,1,0,0:0:0:0:
+476,212,87482,2,0,L|472:284,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
+324,132,88420,6,0,P|320:200|316:284,1,144,4|2,0:3|0:3,0:0:0:0:
+372,344,89123,1,0,0:0:0:0:
+372,344,89357,2,0,B|340:368|292:360|292:360|224:348,1,144,0|2,0:0|0:3,0:0:0:0:
+148,332,90060,1,0,0:0:0:0:
+204,268,90295,6,0,B|212:216|212:216|184:128,1,144,4|2,0:3|0:3,0:0:0:0:
+172,44,90998,1,0,0:0:0:0:
+252,76,91232,2,0,L|324:80,1,72,0|2,0:0|0:3,0:0:0:0:
+412,88,91701,1,2,0:3:0:0:
+377,9,91935,1,0,0:0:0:0:
+360,160,92170,6,0,P|352:232|380:296,1,144,4|2,0:3|0:3,0:0:0:0:
+456,336,92873,1,0,0:0:0:0:
+452,248,93107,2,0,L|456:176,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
+288,308,94045,6,0,P|223:280|152:288,1,144,4|2,0:3|0:3,0:0:0:0:
+80,336,94748,1,0,0:0:0:0:
+72,252,94982,2,0,B|80:228|80:228|76:180,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
+64,80,95920,6,0,B|100:69|132:92|132:92|164:115|200:104,1,144,4|2,0:3|0:3,0:0:0:0:
+180,20,96623,1,0,0:0:0:0:
+265,45,96857,2,0,P|282:76|287:111,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
+200,104,97560,1,0,0:0:0:0:
+200,104,97677,1,0,0:0:0:0:
+200,104,97795,6,0,B|196:142|217:166|217:166|176:180|160:220,1,144,4|0,0:3|0:0,0:0:0:0:
+240,248,98966,5,0,0:0:0:0:
+202,325,99201,2,0,P|254:333|301:309,1,108,0|0,0:0|0:0,0:0:0:0:
+315,292,99670,6,0,L|323:220,1,72,4|2,0:0|0:0,0:0:0:0:
+365,144,100138,2,0,P|379:112|380:76,1,72,0|2,1:0|0:0,0:0:0:0:
+296,92,100607,2,0,B|252:76|252:76|156:92,1,144,4|0,2:3|1:0,1:0:0:0:
+156,92,101310,1,2,0:0:0:0:
+76,57,101545,6,0,P|71:92|77:127,1,72,4|2,0:3|0:0,0:0:0:0:
+148,180,102013,2,0,P|176:240|160:316,1,144,0|4,1:0|2:3,1:0:0:0:
+108,384,102716,1,2,0:0:0:0:
+72,304,102951,2,0,B|48:300|48:300|0:304,1,72,0|2,1:0|0:0,0:0:0:0:
+161,313,103420,6,0,B|216:316|216:316|244:308|244:308|316:312,1,144,4|0,2:3|1:0,1:0:0:0:
+392,316,104123,1,2,0:0:0:0:
+352,240,104357,2,0,P|340:204|344:152,1,72,4|2,0:3|0:0,0:0:0:0:
+436,120,104826,2,0,P|436:156|424:190,1,72,0|2,1:0|0:0,0:0:0:0:
+352,240,105295,5,4,0:3:0:0:
+268,220,105529,1,2,0:0:0:0:
+244,136,105763,2,0,L|168:132,1,72,0|2,1:0|0:0,0:0:0:0:
+104,228,106232,2,0,L|176:225,1,72,4|2,0:3|0:0,0:0:0:0:
+300,312,106701,2,0,L|224:308,1,72,0|2,1:0|0:0,0:0:0:0:
+112,312,107170,6,0,P|48:288|24:212,1,144,4|0,2:0|1:0,1:0:0:0:
+36,136,107873,1,2,0:0:0:0:
+76,60,108107,2,0,L|228:64,1,144,4|0,2:3|1:0,1:0:0:0:
+308,68,108810,1,2,0:0:0:0:
+385,29,109045,6,0,P|392:63|388:99,1,72,4|2,0:3|0:0,0:0:0:0:
+320,152,109513,2,0,L|177:156,1,144,0|4,1:0|2:3,1:0:0:0:
+100,196,110216,1,2,0:0:0:0:
+68,276,110451,2,0,P|60:312|64:348,1,72,0|2,1:0|0:0,0:0:0:0:
+144,316,110920,6,0,B|180:305|212:328|212:328|244:351|280:340,1,144,4|0,2:3|1:0,1:0:0:0:
+360,308,111623,1,2,0:0:0:0:
+436,268,111857,2,0,L|432:116,1,144,4|0,2:3|1:0,1:0:0:0:
+432,124,112560,1,2,0:0:0:0:
+504,76,112795,6,0,P|478:50|443:38,1,72,4|2,0:3|0:0,0:0:0:0:
+364,72,113263,2,0,P|349:106|350:143,1,72,0|2,1:0|0:0,0:0:0:0:
+356,228,113732,5,0,3:0:0:0:
+356,228,113966,1,2,0:0:0:0:
+276,188,114201,2,0,L|200:192,1,72,8|0,0:3|0:0,0:0:0:0:
+144,128,114670,6,0,P|120:168|160:268,1,144,4|2,0:0|1:2,0:0:0:0:
+224,292,115373,1,0,0:0:0:0:
+152,344,115607,2,0,L|80:348,1,72,4|0,0:3|3:0,0:0:0:0:
+224,292,116076,2,0,L|296:296,1,72,2|0,1:2|0:0,0:0:0:0:
+380,312,116545,6,0,P|340:296|312:200,1,144,4|2,0:3|1:2,0:0:0:0:
+369,143,117248,1,0,0:0:0:0:
+447,182,117482,2,0,P|425:210|393:227,1,72,4|0,0:3|3:0,0:0:0:0:
+380,60,117951,2,0,P|414:72|440:96,1,72,2|0,1:2|0:0,0:0:0:0:
+369,143,118420,6,0,L|224:144,1,144,4|2,0:3|1:2,0:0:0:0:
+140,144,119123,1,0,0:0:0:0:
+182,70,119357,2,0,P|194:34|190:-6,1,72,4|0,0:3|3:0,0:0:0:0:
+60,120,119826,2,0,P|59:156|70:190,1,72,2|0,1:2|0:0,0:0:0:0:
+128,256,120295,6,0,B|176:240|176:240|268:256,1,144,4|2,0:3|1:2,0:0:0:0:
+348,224,120998,2,0,P|352:268|344:296,1,72,0|4,0:0|0:3,0:0:0:0:
+268,256,121466,2,0,P|264:212|272:184,1,72,0|2,3:0|1:2,0:0:0:0:
+344,136,121935,1,0,0:0:0:0:
+392,64,122170,6,0,P|316:56|260:112,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0:
+432,140,123341,1,0,3:0:0:0:
+412,224,123576,2,0,L|488:228,1,72,2|0,1:2|0:0,0:0:0:0:
+332,256,124045,6,0,P|263:246|208:290,1,144,4|2,0:3|1:2,0:0:0:0:
+286,327,124748,1,0,0:0:0:0:
+208,290,124982,2,0,L|212:218,1,72,4|0,0:3|3:0,0:0:0:0:
+135,181,125451,2,0,L|124:252,1,72,2|0,1:2|0:0,0:0:0:0:
+44,204,125920,6,0,L|80:56,1,144,4|2,0:3|1:2,0:0:0:0:
+236,132,126857,2,0,P|304:136|372:104,1,144,4|2,0:3|1:2,0:0:0:0:
+428,40,127560,1,0,0:0:0:0:
+456,124,127795,6,0,L|460:196,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
+344,256,128732,2,0,L|340:328,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
+268,216,129435,1,0,0:0:0:0:
+180,220,129670,5,4,2:0:0:0:
+256,40,130373,1,2,0:0:0:0:
+64,68,131076,1,2,0:0:0:0:
+92,136,131310,1,0,0:0:0:0:
+64,204,131545,6,0,L|60:288,1,72
+31,343,132248,2,0,P|86:345|127:309,1,108
+332,220,133420,5,2,0:0:0:0:
+256,40,134123,1,2,0:0:0:0:
+448,68,134826,1,2,0:0:0:0:
+420,136,135060,1,0,0:0:0:0:
+448,204,135295,6,0,L|452:288,1,72,2|0,0:0|0:0,0:0:0:0:
+480,343,135998,2,0,P|426:345|385:309,1,108
+256,192,137170,5,2,0:0:0:0:
+156,360,137873,1,2,0:0:0:0:
+356,360,138576,2,0,L|352:308,1,36,2|0,0:0|0:0,0:0:0:0:
+304,268,139045,6,0,P|336:253|372:252,1,72
+448,260,139748,2,0,L|444:152,1,108
+256,192,140920,5,2,0:0:0:0:
+356,24,141623,1,2,0:0:0:0:
+156,24,142326,2,0,L|160:72,1,36,2|0,0:0|0:0,0:0:0:0:
+208,116,142795,6,0,P|176:131|140:132,1,72,2|0,0:0|0:0,0:0:0:0:
+64,124,143498,2,0,L|68:232,1,108
+68,232,144670,5,4,0:3:0:0:
+216,320,145138,1,4,0:3:0:0:
+304,172,145607,1,4,0:3:0:0:
+156,84,146075,1,4,0:3:0:0:
+296,320,146545,5,4,0:3:0:0:
+208,172,147013,1,4,0:3:0:0:
+356,84,147482,1,4,0:3:0:0:
+444,232,147950,1,4,0:3:0:0:
+296,320,148420,6,0,P|252:328|192:296,2,108.000004119873,4|4|4,0:3|0:3|0:3,0:0:0:0:
+260,248,149591,1,0,0:0:0:0:
+320,196,149826,2,0,L|316:140,1,54.0000020599366,4|0,0:3|0:0,0:0:0:0:
+120,236,159670,6,0,L|176:232,1,54.0000020599366,4|0,0:3|0:0,0:0:0:0:
+160,152,160138,2,0,L|104:156,1,54.0000020599366,2|0,0:0|0:0,0:0:0:0:
+240,180,160607,2,0,P|292:188|344:172,1,108.000004119873,4|2,0:3|0:0,3:0:0:0:
+408,120,161310,1,0,3:0:0:0:
+424,200,161545,6,0,L|420:256,1,54.0000020599366,4|0,0:3|0:0,0:0:0:0:
+376,320,162013,2,0,P|396:328|480:304,2,108.000004119873,2|6|2,2:0|0:3|2:0,3:0:0:0:
+312,268,163185,1,0,0:0:0:0:
+296,348,163420,6,0,L|240:344,1,54.0000020599366,4|0,3:0|3:0,0:0:0:0:
+160,320,163888,2,0,L|100:316,1,57.6,4|0,3:0|3:0,0:0:0:0:
+64,232,164357,6,0,L|128:228,1,61.2000011672974,4|0,3:0|3:0,0:0:0:0:
+204,200,164825,2,0,L|268:196,1,61.2000011672974,4|0,3:0|3:0,0:0:0:0:
+232,108,165295,6,0,L|164:104,1,68.399998173523,4|0,3:0|3:0,0:0:0:0:
+80,84,165763,2,0,L|4:80,1,72,4|0,3:0|3:0,0:0:0:0:
+324,120,167170,6,0,P|388:128|456:92,1,144,4|2,0:0|1:2,0:0:0:0:
+496,168,167873,1,0,0:0:0:0:
+496,168,168107,2,0,P|484:204|488:256,1,72,4|0,0:3|3:0,0:0:0:0:
+408,296,168576,2,0,P|398:261|378:231,1,72,2|0,1:2|0:0,0:0:0:0:
+296,200,169045,6,0,B|228:228|156:204,1,144,4|2,0:3|1:2,0:0:0:0:
+84,156,169748,1,0,0:0:0:0:
+80,244,169982,2,0,L|76:316,1,72,4|0,0:3|3:0,0:0:0:0:
+170,274,170451,2,0,L|156:204,1,72,2|0,1:2|0:0,0:0:0:0:
+216,140,170920,6,0,L|284:276,1,144,4|2,0:3|1:2,0:0:0:0:
+320,344,171623,1,0,0:0:0:0:
+372,276,171857,2,0,P|366:240|349:207,1,72,4|0,0:3|3:0,0:0:0:0:
+312,132,172326,2,0,L|276:60,1,72,2|0,1:2|0:0,0:0:0:0:
+208,20,172795,6,0,P|272:36|348:12,1,144,4|2,0:3|1:2,0:0:0:0:
+424,48,173498,2,0,L|412:132,1,72,0|4,0:0|0:3,0:0:0:0:
+484,168,173966,2,0,L|472:252,1,72,0|2,3:0|1:2,0:0:0:0:
+400,280,174435,1,0,0:0:0:0:
+346,348,174670,6,0,P|414:363|472:324,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0:
+312,268,175841,1,0,3:0:0:0:
+256,336,176076,2,0,L|184:332,1,72,2|0,1:2|0:0,0:0:0:0:
+80,244,176545,6,0,B|140:248|140:248|164:244|164:244|223:247,1,144,4|2,0:3|1:2,0:0:0:0:
+312,268,177248,1,0,0:0:0:0:
+224,247,177482,2,0,P|240:215|272:187,1,72,4|0,0:3|3:0,0:0:0:0:
+204,131,177951,2,0,P|233:111|275:103,1,72,2|0,1:2|0:0,0:0:0:0:
+240,23,178420,6,0,B|280:15|316:35|316:35|376:71,1,144,4|2,0:3|1:2,0:0:0:0:
+399,236,179357,2,0,B|359:244|323:224|323:224|263:188,1,144,4|2,0:3|1:2,0:0:0:0:
+204,132,180060,1,0,0:0:0:0:
+184,216,180295,6,0,L|188:288,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
+120,156,180998,1,0,0:0:0:0:
+56,96,181232,2,0,L|60:24,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0:
+36,180,181935,1,0,0:0:0:0:
+100,240,182170,6,0,P|144:300|116:380,2,144,4|2|4,0:0|1:2|0:3,0:0:0:0:
+60,316,183341,1,0,0:0:0:0:
+220,352,183576,2,0,L|308:348,1,72,2|0,1:2|0:0,0:0:0:0:
+396,264,184045,6,0,B|336:268|336:268|312:264|312:264|253:267,1,144,4|2,0:3|1:2,0:0:0:0:
+253,267,184748,1,0,0:0:0:0:
+268,180,184982,2,0,L|339:177,1,72,4|0,0:3|0:0,0:0:0:0:
+164,280,185451,2,0,L|92:282,1,72,2|0,1:2|0:0,0:0:0:0:
+52,208,185920,6,0,P|8:268|32:344,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0:
+140,212,187091,1,0,0:0:0:0:
+92,284,187326,2,0,P|104:316|100:368,1,72,2|0,1:2|0:0,0:0:0:0:
+52,208,187795,6,0,P|48:136|76:72,1,144,4|2,0:3|1:2,0:0:0:0:
+160,52,188498,2,0,P|188:28|220:16,1,72,0|4,0:0|0:3,0:0:0:0:
+232,100,188966,2,0,P|268:93|301:98,1,72,0|2,0:0|1:2,0:0:0:0:
+372,152,189435,1,0,0:0:0:0:
+420,224,189670,6,0,P|428:296|400:360,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0:
+372,152,190841,1,0,0:0:0:0:
+392,68,191076,2,0,L|465:64,1,72,2|0,1:2|0:0,0:0:0:0:
+304,92,191545,6,0,P|236:104|168:76,1,144,4|2,0:3|1:2,0:0:0:0:
+108,12,192248,1,0,0:0:0:0:
+168,76,192482,2,0,L|172:152,1,72,4|0,0:3|0:0,0:0:0:0:
+80,136,192951,2,0,L|101:204,1,72,2|0,1:2|0:0,0:0:0:0:
+12,220,193420,6,0,B|50:279|50:279|80:300|120:292,1,144,4|2,0:3|1:2,0:0:0:0:
+284,232,194357,2,0,B|320:221|352:244|352:244|384:267|420:256,1,144,4|2,0:3|1:2,0:0:0:0:
+488,200,195060,1,0,0:0:0:0:
+507,284,195295,6,0,P|492:315|464:338,1,72,4|0,0:0|0:0,0:0:0:0:
+380,356,195763,2,0,L|236:352,1,144,0|4,1:0|0:3,0:0:0:0:
+152,328,196466,1,0,3:0:0:0:
+64,336,196701,2,0,P|29:325|4:300,1,72,0|0,1:0|0:0,0:0:0:0:
+76,252,197170,6,0,P|108:188|96:116,1,144,4|0,0:0|1:0,0:0:0:0:
+36,56,197873,1,2,0:0:0:0:
+120,32,198107,2,0,L|192:28,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
+248,152,199045,6,0,P|280:168|304:196,1,72,4|2,0:3|0:0,0:0:0:0:
+336,277,199513,2,0,P|306:296|269:303,1,72,2|0,1:2|0:0,0:0:0:0:
+183,290,199982,2,0,P|180:254|193:219,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0:
+436,252,200920,6,0,P|404:188|416:116,1,144,4|0,0:3|1:0,0:0:0:0:
+476,56,201623,1,2,0:0:0:0:
+392,32,201857,2,0,L|320:28,2,72,4|0|2,0:3|0:0|1:2,0:0:0:0:
+264,152,202795,6,0,P|232:168|208:196,1,72,4|2,0:3|0:0,0:0:0:0:
+176,277,203263,2,0,P|205:296|242:303,1,72,2|0,1:2|0:0,0:0:0:0:
+329,290,203732,2,0,P|331:254|318:219,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0:
+72,324,204670,6,0,B|60:272|60:272|76:180,1,144,4|0,0:0|1:0,0:0:0:0:
+92,96,205373,1,2,0:0:0:0:
+8,124,205607,2,0,P|5:88|14:53,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
+168,192,206545,6,0,P|200:174|237:173,1,72,4|2,0:3|0:0,0:0:0:0:
+320,160,207013,2,0,P|318:196|301:229,1,72,2|0,1:2|0:0,0:0:0:0:
+272,307,207482,2,0,P|240:287|221:256,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0:
+440,324,208420,6,0,B|452:272|452:272|436:180,1,144,4|0,0:3|1:0,0:0:0:0:
+420,96,209123,1,2,0:0:0:0:
+504,124,209357,2,0,P|507:88|498:53,2,72,4|0|2,0:3|0:0|1:2,0:0:0:0:
+344,192,210295,6,0,P|311:174|274:173,1,72,4|2,0:3|0:0,0:0:0:0:
+190,156,210763,2,0,P|191:192|208:225,1,72,2|0,1:2|0:0,0:0:0:0:
+288,256,211232,1,4,0:3:0:0:
+132,332,211701,1,0,1:0:0:0:
+28,192,212170,6,0,P|16:120|44:56,1,144,4|0,0:0|1:0,0:0:0:0:
+120,16,212873,1,2,0:0:0:0:
+204,32,213107,2,0,L|304:28,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
+192,204,214045,6,0,P|196:240|216:272,1,72,4|2,0:3|0:0,0:0:0:0:
+298,241,214513,2,0,P|327:219|345:186,1,72,6|0,1:2|0:0,0:0:0:0:
+280,132,214982,2,0,P|246:117|209:118,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0:
+484,192,215920,6,0,P|496:120|468:56,1,144,4|0,0:3|1:0,0:0:0:0:
+392,16,216623,1,2,0:0:0:0:
+308,32,216857,2,0,L|208:28,2,72,4|0|2,0:3|0:0|1:2,0:0:0:0:
+320,204,217795,6,0,P|316:240|296:272,1,72,4|2,0:3|0:0,0:0:0:0:
+213,241,218263,2,0,P|184:219|166:186,1,72,2|0,1:2|0:0,0:0:0:0:
+232,132,218732,2,0,B|260:112|300:116|300:116|384:128,1,144,4|0,0:3|1:0,0:0:0:0:
+348,336,219670,6,0,B|320:356|280:352|280:352|196:340,1,144,4|0,0:0|1:0,0:0:0:0:
+124,328,220373,1,2,0:0:0:0:
+54,276,220607,2,0,P|41:308|39:345,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
+156,80,221545,6,0,L|251:94,1,72,4|2,0:3|0:0,0:0:0:0:
+212,169,222013,2,0,L|148:160,1,64.799998022461,2|0,1:2|0:0,0:0:0:0:
+140,240,222482,2,0,L|216:252,2,57.6,4|2|0,0:3|0:0|1:0,0:0:0:0:
+256,192,223420,12,0,227170,0:0:0:0:
+";
}
}
diff --git a/osu.Desktop.Tests/Visual/TestCaseReplay.cs b/osu.Desktop.Tests/Visual/TestCaseReplay.cs
index 9b2f59e444..50aefb7af7 100644
--- a/osu.Desktop.Tests/Visual/TestCaseReplay.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseReplay.cs
@@ -2,9 +2,9 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
-using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Rulesets;
using osu.Game.Screens.Play;
+using System.Linq;
namespace osu.Desktop.Tests.Visual
{
@@ -12,11 +12,10 @@ namespace osu.Desktop.Tests.Visual
{
public override string Description => @"Testing replay playback.";
- protected override Player CreatePlayer(WorkingBeatmap beatmap)
+ protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
{
- beatmap.Mods.Value = new Mod[] { new OsuModAutoplay() };
-
- return base.CreatePlayer(beatmap);
+ beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
+ return base.CreatePlayer(beatmap, ruleset);
}
}
}
diff --git a/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs b/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs
index 43bd921cfc..fc8f70b7ce 100644
--- a/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs
@@ -3,18 +3,23 @@
using System;
using System.Collections.Generic;
+using NUnit.Framework;
using OpenTK;
using osu.Desktop.Tests.Beatmaps;
+using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
+using osu.Game.Rulesets.Timing;
using osu.Game.Rulesets.UI;
namespace osu.Desktop.Tests.Visual
@@ -22,6 +27,7 @@ namespace osu.Desktop.Tests.Visual
///
/// The most minimal implementation of a playfield with scrolling hit objects.
///
+ [TestFixture]
public class TestCaseScrollingPlayfield : OsuTestCase
{
public TestCaseScrollingPlayfield()
@@ -58,12 +64,72 @@ namespace osu.Desktop.Tests.Visual
AddStep("Reverse direction", () =>
{
- horizontalRulesetContainer.Playfield.Reversed.Toggle();
- verticalRulesetContainer.Playfield.Reversed.Toggle();
+ horizontalRulesetContainer.Playfield.Reverse();
+ verticalRulesetContainer.Playfield.Reverse();
});
}
- private class TestRulesetContainer : ScrollingRulesetContainer
+ [Test]
+ public void TestSpeedAdjustmentOrdering()
+ {
+ var hitObjectContainer = new ScrollingPlayfield.ScrollingHitObjectContainer(Axes.X);
+
+ var speedAdjustments = new[]
+ {
+ new SpeedAdjustmentContainer(new MultiplierControlPoint()),
+ new SpeedAdjustmentContainer(new MultiplierControlPoint(1000)
+ {
+ TimingPoint = new TimingControlPoint { BeatLength = 500 }
+ }),
+ new SpeedAdjustmentContainer(new MultiplierControlPoint(2000)
+ {
+ TimingPoint = new TimingControlPoint { BeatLength = 1000 },
+ DifficultyPoint = new DifficultyControlPoint { SpeedMultiplier = 2}
+ }),
+ new SpeedAdjustmentContainer(new MultiplierControlPoint(3000)
+ {
+ TimingPoint = new TimingControlPoint { BeatLength = 1000 },
+ DifficultyPoint = new DifficultyControlPoint { SpeedMultiplier = 1}
+ }),
+ };
+
+ var hitObjects = new[]
+ {
+ new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = -1000 }),
+ new DrawableTestHitObject(Axes.X, new TestHitObject()),
+ new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 1000 }),
+ new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 2000 }),
+ new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 3000 }),
+ new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 4000 }),
+ };
+
+ hitObjects.ForEach(h => hitObjectContainer.Add(h));
+ speedAdjustments.ForEach(hitObjectContainer.AddSpeedAdjustment);
+
+ // The 0th index in hitObjectContainer.SpeedAdjustments is the "default" control point
+ // Check multiplier of the default speed adjustment
+ Assert.AreEqual(1, hitObjectContainer.SpeedAdjustments[0].ControlPoint.Multiplier);
+ Assert.AreEqual(1, speedAdjustments[0].ControlPoint.Multiplier);
+ Assert.AreEqual(2, speedAdjustments[1].ControlPoint.Multiplier);
+ Assert.AreEqual(2, speedAdjustments[2].ControlPoint.Multiplier);
+ Assert.AreEqual(1, speedAdjustments[3].ControlPoint.Multiplier);
+
+ // Check insertion of hit objects
+ Assert.IsTrue(hitObjectContainer.SpeedAdjustments[4].Contains(hitObjects[0]));
+ Assert.IsTrue(hitObjectContainer.SpeedAdjustments[3].Contains(hitObjects[1]));
+ Assert.IsTrue(hitObjectContainer.SpeedAdjustments[2].Contains(hitObjects[2]));
+ Assert.IsTrue(hitObjectContainer.SpeedAdjustments[1].Contains(hitObjects[3]));
+ Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[4]));
+ Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[5]));
+
+ hitObjectContainer.RemoveSpeedAdjustment(hitObjectContainer.SpeedAdjustments[3]);
+
+ // The hit object contained in this speed adjustment should be resorted into the one occuring before it
+
+ Assert.IsTrue(hitObjectContainer.SpeedAdjustments[3].Contains(hitObjects[1]));
+ }
+
+ private class TestRulesetContainer : ScrollingRulesetContainer
{
private readonly Axes scrollingAxes;
@@ -77,16 +143,18 @@ namespace osu.Desktop.Tests.Visual
public override ScoreProcessor CreateScoreProcessor() => new TestScoreProcessor();
+ public override PassThroughInputManager CreateInputManager() => new PassThroughInputManager();
+
protected override BeatmapConverter CreateBeatmapConverter() => new TestBeatmapConverter();
- protected override Playfield CreatePlayfield() => new TestPlayfield(scrollingAxes);
+ protected override Playfield CreatePlayfield() => new TestPlayfield(scrollingAxes);
- protected override DrawableHitObject GetVisualRepresentation(TestHitObject h) => new DrawableTestHitObject(scrollingAxes, h);
+ protected override DrawableHitObject GetVisualRepresentation(TestHitObject h) => new DrawableTestHitObject(scrollingAxes, h);
}
- private class TestScoreProcessor : ScoreProcessor
+ private class TestScoreProcessor : ScoreProcessor
{
- protected override void OnNewJudgement(TestJudgement judgement)
+ protected override void OnNewJudgement(Judgement judgement)
{
}
}
@@ -101,7 +169,7 @@ namespace osu.Desktop.Tests.Visual
}
}
- private class DrawableTestHitObject : DrawableScrollingHitObject
+ private class DrawableTestHitObject : DrawableScrollingHitObject
{
public DrawableTestHitObject(Axes scrollingAxes, TestHitObject hitObject)
: base(hitObject)
@@ -117,14 +185,12 @@ namespace osu.Desktop.Tests.Visual
});
}
- protected override TestJudgement CreateJudgement() => new TestJudgement();
-
protected override void UpdateState(ArmedState state)
{
}
}
- private class TestPlayfield : ScrollingPlayfield
+ private class TestPlayfield : ScrollingPlayfield
{
protected override Container Content => content;
private readonly Container content;
@@ -142,17 +208,13 @@ namespace osu.Desktop.Tests.Visual
content = new Container { RelativeSizeAxes = Axes.Both }
};
}
+
+ public void Reverse() => Reversed.Toggle();
}
private class TestHitObject : HitObject
{
}
-
- private class TestJudgement : Judgement
- {
- public override string ResultString { get { throw new NotImplementedException(); } }
- public override string MaxResultString { get { throw new NotImplementedException(); } }
- }
}
-}
\ No newline at end of file
+}
diff --git a/osu.Desktop.Tests/Visual/TestCaseStoryboard.cs b/osu.Desktop.Tests/Visual/TestCaseStoryboard.cs
new file mode 100644
index 0000000000..878198e8d2
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseStoryboard.cs
@@ -0,0 +1,90 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using OpenTK.Graphics;
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Timing;
+using osu.Game;
+using osu.Game.Beatmaps;
+using osu.Game.Overlays;
+using osu.Game.Storyboards.Drawables;
+
+namespace osu.Desktop.Tests.Visual
+{
+ internal class TestCaseStoryboard : OsuTestCase
+ {
+ public override string Description => @"Tests storyboards.";
+
+ private readonly Bindable beatmapBacking = new Bindable();
+
+ private readonly Container storyboardContainer;
+ private DrawableStoryboard storyboard;
+
+ public TestCaseStoryboard()
+ {
+ Clock = new FramedClock();
+
+ Add(new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ },
+ storyboardContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ },
+ });
+ Add(new MusicController
+ {
+ Origin = Anchor.TopRight,
+ Anchor = Anchor.TopRight,
+ State = Visibility.Visible,
+ });
+
+ AddStep("Restart", restart);
+ AddToggleStep("Passing", passing => { if (storyboard != null) storyboard.Passing = passing; });
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuGameBase game)
+ {
+ beatmapBacking.BindTo(game.Beatmap);
+ beatmapBacking.ValueChanged += beatmapChanged;
+ }
+
+ private void beatmapChanged(WorkingBeatmap working)
+ => loadStoryboard(working);
+
+ private void restart()
+ {
+ var track = beatmapBacking.Value.Track;
+
+ track.Reset();
+ loadStoryboard(beatmapBacking.Value);
+ track.Start();
+ }
+
+ private void loadStoryboard(WorkingBeatmap working)
+ {
+ if (storyboard != null)
+ storyboardContainer.Remove(storyboard);
+
+ var decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = true };
+ decoupledClock.ChangeSource(working.Track);
+ storyboardContainer.Clock = decoupledClock;
+
+ storyboardContainer.Add(storyboard = working.Beatmap.Storyboard.CreateDrawable());
+ storyboard.Passing = false;
+ }
+ }
+}
diff --git a/osu.Desktop.Tests/Visual/TestCaseTaikoPlayfield.cs b/osu.Desktop.Tests/Visual/TestCaseTaikoPlayfield.cs
index 45be9e800d..764cf7a1f8 100644
--- a/osu.Desktop.Tests/Visual/TestCaseTaikoPlayfield.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseTaikoPlayfield.cs
@@ -14,6 +14,11 @@ using osu.Game.Rulesets.Taiko.UI;
using OpenTK;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps;
+using osu.Desktop.Tests.Beatmaps;
+using System.Collections.Generic;
+using osu.Framework.Allocation;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Objects;
namespace osu.Desktop.Tests.Visual
{
@@ -27,10 +32,11 @@ namespace osu.Desktop.Tests.Visual
protected override double TimePerAction => default_duration * 2;
private readonly Random rng = new Random(1337);
- private readonly TaikoPlayfield playfield;
- private readonly Container playfieldContainer;
+ private TaikoRulesetContainer rulesetContainer;
+ private Container playfieldContainer;
- public TestCaseTaikoPlayfield()
+ [BackgroundDependencyLoader]
+ private void load(RulesetStore rulesets)
{
AddStep("Hit!", () => addHitJudgement(false));
AddStep("Kiai hit", () => addHitJudgement(true));
@@ -51,6 +57,25 @@ namespace osu.Desktop.Tests.Visual
AddStep("Height test 5", () => changePlayfieldSize(5));
AddStep("Reset height", () => changePlayfieldSize(6));
+ var controlPointInfo = new ControlPointInfo();
+ controlPointInfo.TimingPoints.Add(new TimingControlPoint());
+
+ WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap
+ {
+ HitObjects = new List { new CentreHit() },
+ BeatmapInfo = new BeatmapInfo
+ {
+ Difficulty = new BeatmapDifficulty(),
+ Metadata = new BeatmapMetadata
+ {
+ Artist = @"Unknown",
+ Title = @"Sample Beatmap",
+ Author = @"peppy",
+ },
+ },
+ ControlPointInfo = controlPointInfo
+ });
+
var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
Add(playfieldContainer = new Container
@@ -58,12 +83,9 @@ namespace osu.Desktop.Tests.Visual
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
- Height = TaikoPlayfield.DEFAULT_HEIGHT,
+ Height = 768,
Clock = new FramedClock(rateAdjustClock),
- Children = new[]
- {
- playfield = new TaikoPlayfield()
- }
+ Children = new[] { rulesetContainer = new TaikoRulesetContainer(rulesets.GetRuleset(1).CreateInstance(), beatmap, true) }
});
}
@@ -106,7 +128,7 @@ namespace osu.Desktop.Tests.Visual
private void addHitJudgement(bool kiai)
{
- TaikoHitResult hitResult = RNG.Next(2) == 0 ? TaikoHitResult.Good : TaikoHitResult.Great;
+ HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Good : HitResult.Great;
var cpi = new ControlPointInfo();
cpi.EffectPoints.Add(new EffectControlPoint
@@ -117,56 +139,35 @@ namespace osu.Desktop.Tests.Visual
Hit hit = new Hit();
hit.ApplyDefaults(cpi, new BeatmapDifficulty());
- var h = new DrawableTestHit(hit)
- {
- X = RNG.NextSingle(hitResult == TaikoHitResult.Good ? -0.1f : -0.05f, hitResult == TaikoHitResult.Good ? 0.1f : 0.05f),
- Judgement = new TaikoJudgement
- {
- Result = HitResult.Hit,
- TaikoResult = hitResult,
- TimeOffset = 0
- }
- };
+ var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
- playfield.OnJudgement(h);
+ rulesetContainer.Playfield.OnJudgement(h, new TaikoJudgement { Result = hitResult });
if (RNG.Next(10) == 0)
{
- h.Judgement.SecondHit = true;
- playfield.OnJudgement(h);
+ rulesetContainer.Playfield.OnJudgement(h, new TaikoJudgement { Result = hitResult });
+ rulesetContainer.Playfield.OnJudgement(h, new TaikoStrongHitJudgement());
}
}
private void addMissJudgement()
{
- playfield.OnJudgement(new DrawableTestHit(new Hit())
- {
- Judgement = new TaikoJudgement
- {
- Result = HitResult.Miss,
- TimeOffset = 0
- }
- });
+ rulesetContainer.Playfield.OnJudgement(new DrawableTestHit(new Hit()), new TaikoJudgement { Result = HitResult.Miss });
}
private void addBarLine(bool major, double delay = scroll_time)
{
- BarLine bl = new BarLine
- {
- StartTime = playfield.Time.Current + delay,
- ScrollTime = scroll_time
- };
+ BarLine bl = new BarLine { StartTime = rulesetContainer.Playfield.Time.Current + delay };
- playfield.AddBarLine(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl));
+ rulesetContainer.Playfield.Add(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl));
}
private void addSwell(double duration = default_duration)
{
- playfield.Add(new DrawableSwell(new Swell
+ rulesetContainer.Playfield.Add(new DrawableSwell(new Swell
{
- StartTime = playfield.Time.Current + scroll_time,
+ StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
Duration = duration,
- ScrollTime = scroll_time
}));
}
@@ -177,54 +178,49 @@ namespace osu.Desktop.Tests.Visual
var d = new DrumRoll
{
- StartTime = playfield.Time.Current + scroll_time,
+ StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
IsStrong = strong,
Duration = duration,
- ScrollTime = scroll_time,
};
- playfield.Add(new DrawableDrumRoll(d));
+ rulesetContainer.Playfield.Add(new DrawableDrumRoll(d));
}
private void addCentreHit(bool strong)
{
Hit h = new Hit
{
- StartTime = playfield.Time.Current + scroll_time,
- ScrollTime = scroll_time,
+ StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
IsStrong = strong
};
if (strong)
- playfield.Add(new DrawableCentreHitStrong(h));
+ rulesetContainer.Playfield.Add(new DrawableCentreHitStrong(h));
else
- playfield.Add(new DrawableCentreHit(h));
+ rulesetContainer.Playfield.Add(new DrawableCentreHit(h));
}
private void addRimHit(bool strong)
{
Hit h = new Hit
{
- StartTime = playfield.Time.Current + scroll_time,
- ScrollTime = scroll_time,
+ StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
IsStrong = strong
};
if (strong)
- playfield.Add(new DrawableRimHitStrong(h));
+ rulesetContainer.Playfield.Add(new DrawableRimHitStrong(h));
else
- playfield.Add(new DrawableRimHit(h));
+ rulesetContainer.Playfield.Add(new DrawableRimHit(h));
}
- private class DrawableTestHit : DrawableHitObject
+ private class DrawableTestHit : DrawableHitObject
{
public DrawableTestHit(TaikoHitObject hitObject)
: base(hitObject)
{
}
- protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
-
protected override void UpdateState(ArmedState state)
{
}
diff --git a/osu.Desktop.Tests/Visual/TestCaseUserProfile.cs b/osu.Desktop.Tests/Visual/TestCaseUserProfile.cs
index e5955441dc..a94177c1b6 100644
--- a/osu.Desktop.Tests/Visual/TestCaseUserProfile.cs
+++ b/osu.Desktop.Tests/Visual/TestCaseUserProfile.cs
@@ -33,13 +33,10 @@ namespace osu.Desktop.Tests.Visual
Rank = 2148,
PP = 4567.89m
},
- AllRankHistories = new User.RankHistories
+ RankHistory = new User.RankHistoryData
{
- Osu = new User.RankHistory
- {
- Mode = @"osu",
- Data = Enumerable.Range(2345,45).Concat(Enumerable.Range(2109,40)).ToArray()
- }
+ Mode = @"osu",
+ Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray()
}
}, false));
AddStep("Show ppy", () => profile.ShowUser(new User
diff --git a/osu.Desktop.Tests/osu.Desktop.Tests.csproj b/osu.Desktop.Tests/osu.Desktop.Tests.csproj
index 24d112a45c..f894b25f06 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
@@ -35,14 +35,15 @@
- $(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll
+ $(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll
+ True
-
- $(SolutionDir)\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll
+
+ $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll
True
- $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
True
@@ -87,9 +88,11 @@
+
+
@@ -155,6 +158,7 @@
osu.licenseheader
+
diff --git a/osu.Desktop.Tests/packages.config b/osu.Desktop.Tests/packages.config
index ed487e5cd5..ea33822638 100644
--- a/osu.Desktop.Tests/packages.config
+++ b/osu.Desktop.Tests/packages.config
@@ -4,9 +4,9 @@ Copyright (c) 2007-2017 ppy Pty Ltd .
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
-
-
-
+
+
+
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/OpenTK.dll.config b/osu.Desktop/OpenTK.dll.config
index 627e9f6009..5620e3d9e2 100644
--- a/osu.Desktop/OpenTK.dll.config
+++ b/osu.Desktop/OpenTK.dll.config
@@ -1,7 +1,3 @@
-
diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index 88c8a206c8..a623347540 100644
--- a/osu.Desktop/OsuGameDesktop.cs
+++ b/osu.Desktop/OsuGameDesktop.cs
@@ -20,16 +20,11 @@ namespace osu.Desktop
{
internal class OsuGameDesktop : OsuGame
{
- private readonly VersionManager versionManager;
+ private VersionManager versionManager;
public OsuGameDesktop(string[] args = null)
: base(args)
{
- versionManager = new VersionManager
- {
- Depth = int.MinValue,
- State = Visibility.Hidden
- };
}
public override Storage GetStorageForStableInstall()
@@ -88,11 +83,15 @@ namespace osu.Desktop
{
base.LoadComplete();
- LoadComponentAsync(versionManager, Add);
+ LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue });
+
ScreenChanged += s =>
{
- if (!versionManager.IsPresent && s is Intro)
+ if (s is Intro && s.ChildScreen == null)
+ {
+ Add(versionManager);
versionManager.State = Visibility.Visible;
+ }
};
}
diff --git a/osu.Desktop/OsuTestBrowser.cs b/osu.Desktop/OsuTestBrowser.cs
index 50af9bd317..23617de1c0 100644
--- a/osu.Desktop/OsuTestBrowser.cs
+++ b/osu.Desktop/OsuTestBrowser.cs
@@ -24,11 +24,6 @@ namespace osu.Desktop
public override void SetHost(GameHost host)
{
base.SetHost(host);
-
- host.UpdateThread.InactiveHz = host.UpdateThread.ActiveHz;
- host.DrawThread.InactiveHz = host.DrawThread.ActiveHz;
- host.InputThread.InactiveHz = host.InputThread.ActiveHz;
-
host.Window.CursorState |= CursorState.Hidden;
}
}
diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs
index b445340f50..1c0c1475b1 100644
--- a/osu.Desktop/Overlays/VersionManager.cs
+++ b/osu.Desktop/Overlays/VersionManager.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
+using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
@@ -19,6 +20,7 @@ using OpenTK.Graphics;
using System.Net.Http;
using osu.Framework.Logging;
using osu.Game;
+using osu.Game.Configuration;
namespace osu.Desktop.Overlays
{
@@ -26,17 +28,22 @@ namespace osu.Desktop.Overlays
{
private UpdateManager updateManager;
private NotificationOverlay notificationOverlay;
+ private OsuConfigManager config;
+ private OsuGameBase game;
public override bool HandleInput => false;
[BackgroundDependencyLoader]
- private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game)
+ private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config)
{
notificationOverlay = notification;
+ this.config = config;
+ this.game = game;
AutoSizeAxes = Axes.Both;
Anchor = Anchor.BottomCentre;
Origin = Anchor.BottomCentre;
+
Alpha = 0;
Children = new Drawable[]
@@ -91,6 +98,42 @@ namespace osu.Desktop.Overlays
checkForUpdateAsync();
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ var version = game.Version;
+ var lastVersion = config.Get(OsuSetting.Version);
+ if (game.IsDeployedBuild && version != lastVersion)
+ {
+ config.Set(OsuSetting.Version, version);
+
+ // only show a notification if we've previously saved a version to the config file (ie. not the first run).
+ if (!string.IsNullOrEmpty(lastVersion))
+ Scheduler.AddDelayed(() => notificationOverlay.Post(new UpdateCompleteNotification(version)), 5000);
+ }
+ }
+
+ private class UpdateCompleteNotification : SimpleNotification
+ {
+ public UpdateCompleteNotification(string version)
+ {
+ Text = $"You are now running osu!lazer {version}.\nClick to see what's new!";
+ Icon = FontAwesome.fa_check_square;
+ Activated = delegate
+ {
+ Process.Start($"https://github.com/ppy/osu/releases/tag/v{version}");
+ return true;
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ IconBackgound.Colour = colours.BlueDark;
+ }
+ }
+
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
diff --git a/osu.Desktop/app.config b/osu.Desktop/app.config
new file mode 100644
index 0000000000..a704cc3750
--- /dev/null
+++ b/osu.Desktop/app.config
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index bbca4145c6..87d9ce8eec 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
@@ -69,7 +69,8 @@
none
true
bin\Release\
- CuttingEdge NoUpdate
+
+
prompt
4
true
@@ -104,6 +105,7 @@
prompt
AllRules.ruleset
--tests
+ false
@@ -136,36 +138,28 @@
- $(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\NuGet.Squirrel.dll
+ $(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\NuGet.Squirrel.dll
True
- $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
True
-
- $(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll
+
+ $(SolutionDir)\packages\SharpCompress.0.18.1\lib\net45\SharpCompress.dll
True
$(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll
True
-
- $(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\Squirrel.dll
+
+ $(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\Squirrel.dll
True
-
- $(SolutionDir)\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll
- True
-
-
- $(SolutionDir)\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll
- True
-
@@ -173,6 +167,7 @@
osu.licenseheader
+
diff --git a/osu.Desktop/packages.config b/osu.Desktop/packages.config
index 3ad2106d2b..58f9102aa1 100644
--- a/osu.Desktop/packages.config
+++ b/osu.Desktop/packages.config
@@ -5,10 +5,9 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
-->
-
-
-
+
+
-
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Catch/CatchInputManager.cs b/osu.Game.Rulesets.Catch/CatchInputManager.cs
index 2a913a77d3..47e9b9905a 100644
--- a/osu.Game.Rulesets.Catch/CatchInputManager.cs
+++ b/osu.Game.Rulesets.Catch/CatchInputManager.cs
@@ -3,11 +3,11 @@
using System.ComponentModel;
using osu.Framework.Input.Bindings;
-using osu.Game.Input.Bindings;
+using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch
{
- public class CatchInputManager : DatabasedKeyBindingInputManager
+ public class CatchInputManager : RulesetInputManager
{
public CatchInputManager(RulesetInfo ruleset)
: base(ruleset, 0, SimultaneousBindingMode.Unique)
diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs
index b486566b7d..1a9b034cf2 100644
--- a/osu.Game.Rulesets.Catch/CatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs
@@ -1,18 +1,14 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using OpenTK.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Catch.Mods;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
-using osu.Game.Screens.Play;
using System.Collections.Generic;
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Catch.Scoring;
-using osu.Game.Rulesets.Scoring;
using osu.Framework.Input.Bindings;
namespace osu.Game.Rulesets.Catch
@@ -23,12 +19,12 @@ namespace osu.Game.Rulesets.Catch
public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[]
{
- new KeyBinding(Key.Z, CatchAction.MoveLeft),
- new KeyBinding(Key.Left, CatchAction.MoveLeft),
- new KeyBinding(Key.X, CatchAction.MoveRight),
- new KeyBinding(Key.Right, CatchAction.MoveRight),
- new KeyBinding(Key.LShift, CatchAction.Dash),
- new KeyBinding(Key.RShift, CatchAction.Dash),
+ new KeyBinding(InputKey.Z, CatchAction.MoveLeft),
+ new KeyBinding(InputKey.Left, CatchAction.MoveLeft),
+ new KeyBinding(InputKey.X, CatchAction.MoveRight),
+ new KeyBinding(InputKey.Right, CatchAction.MoveRight),
+ new KeyBinding(InputKey.Shift, CatchAction.Dash),
+ new KeyBinding(InputKey.Shift, CatchAction.Dash),
};
public override IEnumerable GetModsFor(ModType type)
@@ -95,23 +91,12 @@ 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 };
- public override IEnumerable CreateGameplayKeys() => new KeyCounter[]
- {
- new KeyCounterKeyboard(Key.ShiftLeft),
- new KeyCounterMouse(MouseButton.Left),
- new KeyCounterMouse(MouseButton.Right)
- };
-
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new CatchDifficultyCalculator(beatmap);
- public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor();
-
public override int LegacyID => 2;
public CatchRuleset(RulesetInfo rulesetInfo)
diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs
index f0125b4c3a..cc5b1eaaf4 100644
--- a/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs
+++ b/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs
@@ -7,8 +7,6 @@ namespace osu.Game.Rulesets.Catch.Judgements
{
public class CatchJudgement : Judgement
{
- public override string ResultString => string.Empty;
-
- public override string MaxResultString => string.Empty;
+ // todo: wangs
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs
index 62e5c094be..3dd086fb48 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs
@@ -9,14 +9,14 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Catch.Judgements;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
- public class DrawableFruit : DrawableScrollingHitObject
+ public class DrawableFruit : DrawableScrollingHitObject
{
private const float pulp_size = 30;
@@ -98,14 +98,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
};
}
- protected override CatchJudgement CreateJudgement() => new CatchJudgement();
-
private const float preempt = 1000;
- protected override void CheckJudgement(bool userTriggered)
+ protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
- if (Judgement.TimeOffset > 0)
- Judgement.Result = CheckPosition?.Invoke(HitObject) ?? false ? HitResult.Hit : HitResult.Miss;
+ if (timeOffset > 0)
+ AddJudgement(new Judgement { Result = CheckPosition?.Invoke(HitObject) ?? false ? HitResult.Perfect : HitResult.Miss });
}
protected override void UpdateState(ArmedState state)
diff --git a/osu.Game.Rulesets.Catch/OpenTK.dll.config b/osu.Game.Rulesets.Catch/OpenTK.dll.config
index 627e9f6009..5620e3d9e2 100644
--- a/osu.Game.Rulesets.Catch/OpenTK.dll.config
+++ b/osu.Game.Rulesets.Catch/OpenTK.dll.config
@@ -1,7 +1,3 @@
-
diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
index 33c1355823..11e95622ca 100644
--- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
@@ -1,34 +1,21 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch.Scoring
{
- internal class CatchScoreProcessor : ScoreProcessor
+ internal class CatchScoreProcessor : ScoreProcessor
{
public CatchScoreProcessor()
{
}
- public CatchScoreProcessor(RulesetContainer rulesetContainer)
+ public CatchScoreProcessor(RulesetContainer rulesetContainer)
: base(rulesetContainer)
{
}
-
- protected override void Reset()
- {
- base.Reset();
-
- Health.Value = 1;
- Accuracy.Value = 1;
- }
-
- protected override void OnNewJudgement(CatchJudgement judgement)
- {
- }
}
}
diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
index 4ea6d180f3..2b6f9bbf5a 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
@@ -2,17 +2,16 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.UI;
using OpenTK;
-using osu.Game.Rulesets.Catch.Judgements;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Catch.Objects.Drawable;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Catch.UI
{
- public class CatchPlayfield : ScrollingPlayfield
+ public class CatchPlayfield : ScrollingPlayfield
{
protected override Container Content => content;
private readonly Container content;
@@ -44,22 +43,23 @@ namespace osu.Game.Rulesets.Catch.UI
};
}
- public override void Add(DrawableHitObject h)
+ public override void Add(DrawableHitObject h)
{
+ h.Depth = (float)h.HitObject.StartTime;
+
base.Add(h);
var fruit = (DrawableFruit)h;
fruit.CheckPosition = catcherArea.CheckIfWeCanCatch;
- fruit.OnJudgement += Fruit_OnJudgement;
}
- private void Fruit_OnJudgement(DrawableHitObject obj)
+ public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
- if (obj.Judgement.Result == HitResult.Hit)
+ if (judgement.IsHit)
{
- Vector2 screenPosition = obj.ScreenSpaceDrawQuad.Centre;
- Remove(obj);
- catcherArea.Add(obj, screenPosition);
+ Vector2 screenPosition = judgedObject.ScreenSpaceDrawQuad.Centre;
+ Remove(judgedObject);
+ catcherArea.Add(judgedObject, screenPosition);
}
}
}
diff --git a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs
index 1d037deaef..8f332bdcbf 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs
@@ -5,7 +5,6 @@ using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Catch.Beatmaps;
-using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.Scoring;
@@ -15,7 +14,7 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch.UI
{
- public class CatchRulesetContainer : ScrollingRulesetContainer
+ public class CatchRulesetContainer : ScrollingRulesetContainer
{
public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset)
@@ -26,11 +25,11 @@ namespace osu.Game.Rulesets.Catch.UI
protected override BeatmapConverter CreateBeatmapConverter() => new CatchBeatmapConverter();
- protected override Playfield CreatePlayfield() => new CatchPlayfield();
+ protected override Playfield CreatePlayfield() => new CatchPlayfield();
- public override PassThroughInputManager CreateKeyBindingInputManager() => new CatchInputManager(Ruleset?.RulesetInfo);
+ public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
- protected override DrawableHitObject GetVisualRepresentation(CatchBaseHit h)
+ protected override DrawableHitObject GetVisualRepresentation(CatchBaseHit h)
{
if (h is Fruit)
return new DrawableFruit(h);
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index f416f6acfb..5fc2cf9ef7 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -10,7 +10,6 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Bindings;
using osu.Framework.MathUtils;
-using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Objects.Drawables;
@@ -22,7 +21,7 @@ namespace osu.Game.Rulesets.Catch.UI
{
private Catcher catcher;
- public void Add(DrawableHitObject fruit, Vector2 screenPosition) => catcher.AddToStack(fruit, screenPosition);
+ public void Add(DrawableHitObject fruit, Vector2 screenPosition) => catcher.AddToStack(fruit, screenPosition);
public bool CheckIfWeCanCatch(CatchBaseHit obj) => Math.Abs(catcher.Position.X - obj.Position) < catcher.DrawSize.X / DrawSize.X / 2;
@@ -85,7 +84,7 @@ namespace osu.Game.Rulesets.Catch.UI
var additive = createCatcherSprite();
additive.RelativePositionAxes = Axes.Both;
- additive.BlendingMode = BlendingMode.Additive;
+ additive.Blending = BlendingMode.Additive;
additive.Position = Position;
additive.Scale = Scale;
@@ -152,7 +151,7 @@ namespace osu.Game.Rulesets.Catch.UI
X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime / 1800 * speed, 0, 1);
}
- public void AddToStack(DrawableHitObject fruit, Vector2 absolutePosition)
+ public void AddToStack(DrawableHitObject fruit, Vector2 absolutePosition)
{
fruit.RelativePositionAxes = Axes.None;
fruit.Position = new Vector2(ToLocalSpace(absolutePosition).X - DrawSize.X / 2, 0);
@@ -161,11 +160,10 @@ namespace osu.Game.Rulesets.Catch.UI
fruit.Origin = Anchor.BottomCentre;
fruit.Scale *= 0.7f;
fruit.LifetimeEnd = double.MaxValue;
- fruit.Depth = (float)Time.Current;
float distance = fruit.DrawSize.X / 2 * fruit.Scale.X;
- while (Children.OfType().Any(f => Vector2.DistanceSquared(f.Position, fruit.Position) < distance * distance))
+ while (Children.OfType().Any(f => Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance))
{
fruit.X += RNG.Next(-5, 5);
fruit.Y -= RNG.Next(0, 5);
diff --git a/osu.Game.Rulesets.Catch/app.config b/osu.Game.Rulesets.Catch/app.config
new file mode 100644
index 0000000000..faeaf001de
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
index 79ef5f4ba8..18e1ee29ca 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
@@ -34,7 +34,7 @@
- $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
True
@@ -69,6 +69,7 @@
osu.licenseheader
+
diff --git a/osu.Game.Rulesets.Catch/packages.config b/osu.Game.Rulesets.Catch/packages.config
index fa6edb9c8f..0b1838ceee 100644
--- a/osu.Game.Rulesets.Catch/packages.config
+++ b/osu.Game.Rulesets.Catch/packages.config
@@ -1,9 +1,4 @@
-
-
-
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
index b55b9fdb37..be670936fd 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
@@ -28,12 +28,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
private Pattern lastPattern = new Pattern();
private FastRandom random;
private Beatmap beatmap;
- private bool isForCurrentRuleset;
- protected override Beatmap ConvertBeatmap(Beatmap original, bool isForCurrentRuleset)
+ private readonly int availableColumns;
+ private readonly bool isForCurrentRuleset;
+
+ public ManiaBeatmapConverter(bool isForCurrentRuleset, int availableColumns)
{
- this.isForCurrentRuleset = isForCurrentRuleset;
+ if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
+ this.isForCurrentRuleset = isForCurrentRuleset;
+ this.availableColumns = availableColumns;
+ }
+
+ protected override Beatmap ConvertBeatmap(Beatmap original)
+ {
beatmap = original;
BeatmapDifficulty difficulty = original.BeatmapInfo.Difficulty;
@@ -41,7 +49,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
random = new FastRandom(seed);
- return base.ConvertBeatmap(original, isForCurrentRuleset);
+ return base.ConvertBeatmap(original);
}
protected override IEnumerable ConvertHitObject(HitObject original, Beatmap beatmap)
@@ -89,7 +97,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// The hit objects generated.
private IEnumerable generateSpecific(HitObject original)
{
- var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern);
+ var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, availableColumns, lastPattern);
Pattern newPattern = generator.Generate();
lastPattern = newPattern;
@@ -113,14 +121,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
Patterns.PatternGenerator conversion = null;
if (distanceData != null)
- conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern);
+ conversion = new DistanceObjectPatternGenerator(random, original, beatmap, availableColumns, lastPattern);
else if (endTimeData != null)
- conversion = new EndTimeObjectPatternGenerator(random, original, beatmap);
+ conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, availableColumns);
else if (positionData != null)
{
computeDensity(original.StartTime);
- conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair);
+ conversion = new HitObjectPatternGenerator(random, original, beatmap, availableColumns, lastPattern, lastTime, lastPosition, density, lastStair);
recordNote(original.StartTime, positionData.Position);
}
@@ -142,8 +150,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
///
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
{
- public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
- : base(random, hitObject, beatmap, previousPattern)
+ public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
+ : base(random, hitObject, beatmap, availableColumns, previousPattern)
{
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
index 7e9615a703..20966a75f7 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
@@ -29,8 +29,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private PatternType convertType;
- public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
- : base(random, hitObject, beatmap, previousPattern)
+ public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
+ : base(random, hitObject, beatmap, availableColumns, previousPattern)
{
convertType = PatternType.None;
if (Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
@@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// The true distance, accounting for any repeats
double distance = (distanceData?.Distance ?? 0) * repeatCount;
// The velocity of the osu! hit object - calculated as the velocity of a slider
- double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier / (timingPoint.BeatLength * difficultyPoint.SpeedMultiplier);
+ double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength;
// The duration of the osu! hit object
double osuDuration = distance / osuVelocity;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
index 6ad7489e0f..c353496410 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
@@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
private readonly double endTime;
- public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap)
- : base(random, hitObject, beatmap, new Pattern())
+ public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns)
+ : base(random, hitObject, beatmap, availableColumns, new Pattern())
{
var endtimeData = HitObject as IHasEndTime;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
index 99f55cb98b..077b926635 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
@@ -20,9 +20,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private readonly PatternType convertType;
- public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair)
- : base(random, hitObject, beatmap, previousPattern)
+ public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair)
+ : base(random, hitObject, beatmap, availableColumns, previousPattern)
{
+ if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));
+ if (density < 0) throw new ArgumentOutOfRangeException(nameof(density));
+
StairType = lastStair;
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
index b72618c36c..a3173f9784 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
@@ -25,11 +25,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
///
protected readonly FastRandom Random;
- protected PatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
- : base(hitObject, beatmap, previousPattern)
+ protected PatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
+ : base(hitObject, beatmap, availableColumns, previousPattern)
{
- Random = random;
+ if (random == null) throw new ArgumentNullException(nameof(random));
+ if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
+ if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
+ if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
+ Random = random;
RandomStart = AvailableColumns == 8 ? 1 : 0;
}
@@ -62,6 +66,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// The amount of notes to be generated.
protected int GetRandomNoteCount(double p2, double p3, double p4 = 0, double p5 = 0, double p6 = 0)
{
+ if (p2 < 0 || p2 > 1) throw new ArgumentOutOfRangeException(nameof(p2));
+ if (p3 < 0 || p3 > 1) throw new ArgumentOutOfRangeException(nameof(p3));
+ if (p4 < 0 || p4 > 1) throw new ArgumentOutOfRangeException(nameof(p4));
+ if (p5 < 0 || p5 > 1) throw new ArgumentOutOfRangeException(nameof(p5));
+ if (p6 < 0 || p6 > 1) throw new ArgumentOutOfRangeException(nameof(p6));
+
double val = Random.NextDouble();
if (val >= 1 - p6)
return 6;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs
index dda4d07182..ef321232c8 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs
@@ -32,13 +32,17 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
///
protected readonly Beatmap Beatmap;
- protected PatternGenerator(HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
+ protected PatternGenerator(HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
{
- PreviousPattern = previousPattern;
+ if (hitObject == null) throw new ArgumentNullException(nameof(hitObject));
+ if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
+ if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
+ if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
+
HitObject = hitObject;
Beatmap = beatmap;
-
- AvailableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize);
+ AvailableColumns = availableColumns;
+ PreviousPattern = previousPattern;
}
///
diff --git a/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs b/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs
index 52b55a4ff5..d7bfa9caa1 100644
--- a/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs
+++ b/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Judgements
{
@@ -145,18 +146,18 @@ namespace osu.Game.Rulesets.Mania.Judgements
///
/// The time offset.
/// The hit result, or null if the time offset results in a miss.
- public ManiaHitResult? ResultFor(double hitOffset)
+ public HitResult? ResultFor(double hitOffset)
{
if (hitOffset <= Perfect / 2)
- return ManiaHitResult.Perfect;
+ return HitResult.Perfect;
if (hitOffset <= Great / 2)
- return ManiaHitResult.Great;
+ return HitResult.Great;
if (hitOffset <= Good / 2)
- return ManiaHitResult.Good;
+ return HitResult.Good;
if (hitOffset <= Ok / 2)
- return ManiaHitResult.Ok;
+ return HitResult.Ok;
if (hitOffset <= Bad / 2)
- return ManiaHitResult.Bad;
+ return HitResult.Meh;
return null;
}
diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs
index d5cf57a5da..a8d1b079eb 100644
--- a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs
+++ b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs
@@ -1,6 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Objects.Drawables;
+
namespace osu.Game.Rulesets.Mania.Judgements
{
public class HoldNoteTailJudgement : ManiaJudgement
@@ -10,27 +12,15 @@ namespace osu.Game.Rulesets.Mania.Judgements
///
public bool HasBroken;
- public override int NumericResultForScore(ManiaHitResult result)
+ protected override int NumericResultFor(HitResult result)
{
switch (result)
{
default:
- return base.NumericResultForScore(result);
- case ManiaHitResult.Great:
- case ManiaHitResult.Perfect:
- return base.NumericResultForScore(HasBroken ? ManiaHitResult.Good : result);
- }
- }
-
- public override int NumericResultForAccuracy(ManiaHitResult result)
- {
- switch (result)
- {
- default:
- return base.NumericResultForAccuracy(result);
- case ManiaHitResult.Great:
- case ManiaHitResult.Perfect:
- return base.NumericResultForAccuracy(HasBroken ? ManiaHitResult.Good : result);
+ return base.NumericResultFor(result);
+ case HitResult.Great:
+ case HitResult.Perfect:
+ return base.NumericResultFor(HasBroken ? HitResult.Good : result);
}
}
}
diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs
index 852f97b3f2..d326c6fc0a 100644
--- a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs
+++ b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs
@@ -1,13 +1,14 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Objects.Drawables;
+
namespace osu.Game.Rulesets.Mania.Judgements
{
public class HoldNoteTickJudgement : ManiaJudgement
{
public override bool AffectsCombo => false;
- public override int NumericResultForScore(ManiaHitResult result) => 20;
- public override int NumericResultForAccuracy(ManiaHitResult result) => 0; // Don't count ticks into accuracy
+ protected override int NumericResultFor(HitResult result) => 20;
}
}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/Judgements/ManiaHitResult.cs b/osu.Game.Rulesets.Mania/Judgements/ManiaHitResult.cs
deleted file mode 100644
index 207a1fb251..0000000000
--- a/osu.Game.Rulesets.Mania/Judgements/ManiaHitResult.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System.ComponentModel;
-
-namespace osu.Game.Rulesets.Mania.Judgements
-{
- public enum ManiaHitResult
- {
- [Description("PERFECT")]
- Perfect,
- [Description("GREAT")]
- Great,
- [Description("GOOD")]
- Good,
- [Description("OK")]
- Ok,
- [Description("BAD")]
- Bad
- }
-}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs
index 33083ca0f5..1f3b352da4 100644
--- a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs
+++ b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs
@@ -8,75 +8,22 @@ namespace osu.Game.Rulesets.Mania.Judgements
{
public class ManiaJudgement : Judgement
{
- ///
- /// The maximum possible hit result.
- ///
- public const ManiaHitResult MAX_HIT_RESULT = ManiaHitResult.Perfect;
-
- ///
- /// The result value for the combo portion of the score.
- ///
- public int ResultValueForScore => Result == HitResult.Miss ? 0 : NumericResultForScore(ManiaResult);
-
- ///
- /// The result value for the accuracy portion of the score.
- ///
- public int ResultValueForAccuracy => Result == HitResult.Miss ? 0 : NumericResultForAccuracy(ManiaResult);
-
- ///
- /// The maximum result value for the combo portion of the score.
- ///
- public int MaxResultValueForScore => NumericResultForScore(MAX_HIT_RESULT);
-
- ///
- /// The maximum result value for the accuracy portion of the score.
- ///
- public int MaxResultValueForAccuracy => NumericResultForAccuracy(MAX_HIT_RESULT);
-
- public override string ResultString => string.Empty;
-
- public override string MaxResultString => string.Empty;
-
- ///
- /// The hit result.
- ///
- public ManiaHitResult ManiaResult;
-
- public virtual int NumericResultForScore(ManiaHitResult result)
+ protected override int NumericResultFor(HitResult result)
{
switch (result)
{
default:
return 0;
- case ManiaHitResult.Bad:
+ case HitResult.Meh:
return 50;
- case ManiaHitResult.Ok:
+ case HitResult.Ok:
return 100;
- case ManiaHitResult.Good:
+ case HitResult.Good:
return 200;
- case ManiaHitResult.Great:
- case ManiaHitResult.Perfect:
+ case HitResult.Great:
+ case HitResult.Perfect:
return 300;
}
}
-
- public virtual int NumericResultForAccuracy(ManiaHitResult result)
- {
- switch (result)
- {
- default:
- return 0;
- case ManiaHitResult.Bad:
- return 50;
- case ManiaHitResult.Ok:
- return 100;
- case ManiaHitResult.Good:
- return 200;
- case ManiaHitResult.Great:
- return 300;
- case ManiaHitResult.Perfect:
- return 305;
- }
- }
}
}
diff --git a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs
index aaba4d94f0..1f01750f44 100644
--- a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs
@@ -6,6 +6,7 @@ using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using System.Collections.Generic;
+using System;
namespace osu.Game.Rulesets.Mania
{
@@ -21,6 +22,6 @@ namespace osu.Game.Rulesets.Mania
return 0;
}
- protected override BeatmapConverter CreateBeatmapConverter() => new ManiaBeatmapConverter();
+ protected override BeatmapConverter CreateBeatmapConverter() => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize)));
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Mania/ManiaInputManager.cs b/osu.Game.Rulesets.Mania/ManiaInputManager.cs
new file mode 100644
index 0000000000..a574bc75ec
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/ManiaInputManager.cs
@@ -0,0 +1,41 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.ComponentModel;
+using osu.Framework.Input.Bindings;
+using osu.Game.Rulesets.UI;
+
+namespace osu.Game.Rulesets.Mania
+{
+ public class ManiaInputManager : RulesetInputManager
+ {
+ public ManiaInputManager(RulesetInfo ruleset, int variant)
+ : base(ruleset, variant, SimultaneousBindingMode.Unique)
+ {
+ }
+ }
+
+ public enum ManiaAction
+ {
+ [Description("Special")]
+ Special,
+ [Description("Key 1")]
+ Key1 = 10,
+ [Description("Key 2")]
+ Key2,
+ [Description("Key 3")]
+ Key3,
+ [Description("Key 4")]
+ Key4,
+ [Description("Key 5")]
+ Key5,
+ [Description("Key 6")]
+ Key6,
+ [Description("Key 7")]
+ Key7,
+ [Description("Key 8")]
+ Key8,
+ [Description("Key 9")]
+ Key9
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index 50ad6960ae..c0996cadf9 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -6,12 +6,10 @@ using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
-using osu.Game.Screens.Play;
using System.Collections.Generic;
using osu.Framework.Graphics;
+using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Mania.Scoring;
-using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania
{
@@ -93,7 +91,7 @@ namespace osu.Game.Rulesets.Mania
{
Mods = new Mod[]
{
- new ModAutoplay(),
+ new ManiaModAutoplay(),
new ModCinema(),
},
},
@@ -105,23 +103,55 @@ 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 };
- public override IEnumerable CreateGameplayKeys() => new KeyCounter[] { /* Todo: Should be keymod specific */ };
-
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new ManiaDifficultyCalculator(beatmap);
- public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor();
-
public override int LegacyID => 3;
public ManiaRuleset(RulesetInfo rulesetInfo)
: base(rulesetInfo)
{
}
+
+ public override IEnumerable AvailableVariants => new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+ public override IEnumerable GetDefaultKeyBindings(int variant = 0)
+ {
+ var leftKeys = new[]
+ {
+ InputKey.A,
+ InputKey.S,
+ InputKey.D,
+ InputKey.F
+ };
+
+ var rightKeys = new[]
+ {
+ InputKey.J,
+ InputKey.K,
+ InputKey.L,
+ InputKey.Semicolon
+ };
+
+ ManiaAction currentKey = ManiaAction.Key1;
+
+ var bindings = new List();
+
+ for (int i = leftKeys.Length - variant / 2; i < leftKeys.Length; i++)
+ bindings.Add(new KeyBinding(leftKeys[i], currentKey++));
+
+ for (int i = 0; i < variant / 2; i++)
+ bindings.Add(new KeyBinding(rightKeys[i], currentKey++));
+
+ if (variant % 2 == 1)
+ bindings.Add(new KeyBinding(InputKey.Space, ManiaAction.Special));
+
+ return bindings;
+ }
+
+ public override string GetVariantName(int variant) => $"{variant}K";
}
}
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs b/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs
index f44ad6fd60..037c3bd567 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs
@@ -4,6 +4,16 @@
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using System;
+using System.Linq;
+using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Framework.MathUtils;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Mania.Replays;
+using osu.Game.Rulesets.Mania.UI;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Users;
+using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Mods
{
@@ -68,6 +78,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;
@@ -75,15 +86,27 @@ namespace osu.Game.Rulesets.Mania.Mods
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
}
- public class ManiaModRandom : Mod
+ public class ManiaModRandom : Mod, IApplicableMod
{
public override string Name => "Random";
+ public override string ShortenedName => "RD";
+ public override FontAwesome Icon => FontAwesome.fa_osu_dice;
public override string Description => @"Shuffle around the notes!";
public override double ScoreMultiplier => 1;
+
+ public void ApplyToRulesetContainer(RulesetContainer rulesetContainer)
+ {
+ int availableColumns = ((ManiaRulesetContainer)rulesetContainer).AvailableColumns;
+
+ var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList();
+
+ rulesetContainer.Objects.OfType().ForEach(h => h.Column = shuffledColumns[h.Column]);
+ }
}
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,8 +169,29 @@ 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;
}
+
+ public class ManiaModAutoplay : ModAutoplay
+ {
+ private int availableColumns;
+
+ public override void ApplyToRulesetContainer(RulesetContainer rulesetContainer)
+ {
+ // Todo: This shouldn't be done, we should be getting a ManiaBeatmap which should store AvailableColumns
+ // But this is dependent on a _lot_ of refactoring
+ var maniaRulesetContainer = (ManiaRulesetContainer)rulesetContainer;
+ availableColumns = maniaRulesetContainer.AvailableColumns;
+
+ base.ApplyToRulesetContainer(rulesetContainer);
+ }
+ protected override Score CreateReplayScore(Beatmap beatmap) => new Score
+ {
+ User = new User { Username = "osu!topus!" },
+ Replay = new ManiaAutoGenerator(beatmap, availableColumns).Generate(),
+ };
+ }
}
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs
index a054e0db56..70270af6c9 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
+using System.Linq;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
@@ -15,6 +16,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;
@@ -23,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public void ApplyToRulesetContainer(ManiaRulesetContainer rulesetContainer, ref List[] hitObjectTimingChanges, ref List barlineTimingChanges)
{
// We have to generate one speed adjustment per hit object for gravity
- foreach (ManiaHitObject obj in rulesetContainer.Objects)
+ foreach (ManiaHitObject obj in rulesetContainer.Objects.OfType())
{
MultiplierControlPoint controlPoint = rulesetContainer.CreateControlPointAt(obj.StartTime);
// Beat length has too large of an effect for gravity, so we'll force it to a constant value for now
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
index 17b0b0a607..237df72480 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
@@ -1,30 +1,32 @@
// 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.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using OpenTK.Graphics;
-using osu.Framework.Configuration;
-using OpenTK.Input;
-using osu.Framework.Input;
using OpenTK;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Framework.Input.Bindings;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
///
/// Visualises a hit object.
///
- public class DrawableHoldNote : DrawableManiaHitObject
+ public class DrawableHoldNote : DrawableManiaHitObject, IKeyBindingHandler
{
private readonly DrawableNote head;
private readonly DrawableNote tail;
+ private readonly GlowPiece glowPiece;
private readonly BodyPiece bodyPiece;
private readonly Container tickContainer;
+ private readonly Container fullHeightContainer;
///
/// Time at which the user started holding this hold note. Null if the user is not holding this hold note.
@@ -36,21 +38,26 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
///
private bool hasBroken;
- public DrawableHoldNote(HoldNote hitObject, Bindable key = null)
- : base(hitObject, key)
+ public DrawableHoldNote(HoldNote hitObject, ManiaAction action)
+ : base(hitObject, action)
{
RelativeSizeAxes = Axes.Both;
Height = (float)HitObject.Duration;
AddRange(new Drawable[]
{
- // For now the body piece covers the entire height of the container
- // whereas possibly in the future we don't want to extend under the head/tail.
- // This will be fixed when new designs are given or the current design is finalized.
+ // The hit object itself cannot be used for various elements because the tail overshoots it
+ // So a specialized container that is updated to contain the tail height is used
+ fullHeightContainer = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ Child = glowPiece = new GlowPiece()
+ },
bodyPiece = new BodyPiece
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
+ RelativeSizeAxes = Axes.X,
},
tickContainer = new Container
{
@@ -58,12 +65,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativeChildOffset = new Vector2(0, (float)HitObject.StartTime),
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
},
- head = new DrawableHeadNote(this, key)
+ head = new DrawableHeadNote(this, action)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
},
- tail = new DrawableTailNote(this, key)
+ tail = new DrawableTailNote(this, action)
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.TopCentre
@@ -96,6 +103,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
tickContainer.Children.ForEach(t => t.AccentColour = value);
+ glowPiece.AccentColour = value;
bodyPiece.AccentColour = value;
head.AccentColour = value;
tail.AccentColour = value;
@@ -106,16 +114,26 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
}
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
+ protected override void Update()
{
- // Make sure the keypress happened within the body of the hold note
+ base.Update();
+
+ // Make the body piece not lie under the head note
+ bodyPiece.Y = head.Height;
+ bodyPiece.Height = DrawHeight - head.Height;
+
+ // Make the fullHeightContainer "contain" the height of the tail note, keeping in mind
+ // that the tail note overshoots the height of this hit object
+ fullHeightContainer.Height = DrawHeight + tail.Height;
+ }
+
+ public bool OnPressed(ManiaAction action)
+ {
+ // Make sure the action happened within the body of the hold note
if (Time.Current < HitObject.StartTime || Time.Current > HitObject.EndTime)
return false;
- if (args.Key != Key)
- return false;
-
- if (args.Repeat)
+ if (action != Action)
return false;
// The user has pressed during the body of the hold note, after the head note and its hit windows have passed
@@ -126,19 +144,19 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return true;
}
- protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
+ public bool OnReleased(ManiaAction action)
{
// Make sure that the user started holding the key during the hold note
if (!holdStartTime.HasValue)
return false;
- if (args.Key != Key)
+ if (action != Action)
return false;
holdStartTime = null;
// If the key has been released too early, the user should not receive full score for the release
- if (!tail.Judged)
+ if (!tail.AllJudged)
hasBroken = true;
return true;
@@ -151,26 +169,28 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
private readonly DrawableHoldNote holdNote;
- public DrawableHeadNote(DrawableHoldNote holdNote, Bindable key = null)
- : base(holdNote.HitObject.Head, key)
+ public DrawableHeadNote(DrawableHoldNote holdNote, ManiaAction action)
+ : base(holdNote.HitObject.Head, action)
{
this.holdNote = holdNote;
RelativePositionAxes = Axes.None;
Y = 0;
+
+ // Life time managed by the parent DrawableHoldNote
+ LifetimeStart = double.MinValue;
+ LifetimeEnd = double.MaxValue;
+
+ GlowPiece.Alpha = 0;
}
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
+ public override bool OnPressed(ManiaAction action)
{
- if (!base.OnKeyDown(state, args))
- return false;
-
- // We only want to trigger a holding state from the head if the head has received a judgement
- if (!Judged)
+ if (!base.OnPressed(action))
return false;
// If the key has been released too early, the user should not receive full score for the release
- if (Judgement.Result == HitResult.Miss)
+ if (Judgements.Any(j => j.Result == HitResult.Miss))
holdNote.hasBroken = true;
// The head note also handles early hits before the body, but we want accurate early hits to count as the body being held
@@ -188,38 +208,58 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
private readonly DrawableHoldNote holdNote;
- public DrawableTailNote(DrawableHoldNote holdNote, Bindable key = null)
- : base(holdNote.HitObject.Tail, key)
+ public DrawableTailNote(DrawableHoldNote holdNote, ManiaAction action)
+ : base(holdNote.HitObject.Tail, action)
{
this.holdNote = holdNote;
RelativePositionAxes = Axes.None;
Y = 0;
+
+ // Life time managed by the parent DrawableHoldNote
+ LifetimeStart = double.MinValue;
+ LifetimeEnd = double.MaxValue;
+
+ GlowPiece.Alpha = 0;
}
- protected override ManiaJudgement CreateJudgement() => new HoldNoteTailJudgement();
-
- protected override void CheckJudgement(bool userTriggered)
+ protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
- base.CheckJudgement(userTriggered);
+ if (!userTriggered)
+ {
+ if (timeOffset > HitObject.HitWindows.Bad / 2)
+ {
+ AddJudgement(new HoldNoteTailJudgement
+ {
+ Result = HitResult.Miss,
+ HasBroken = holdNote.hasBroken
+ });
+ }
- var tailJudgement = Judgement as HoldNoteTailJudgement;
- if (tailJudgement == null)
+ return;
+ }
+
+ double offset = Math.Abs(timeOffset);
+
+ if (offset > HitObject.HitWindows.Miss / 2)
return;
- tailJudgement.HasBroken = holdNote.hasBroken;
+ AddJudgement(new HoldNoteTailJudgement
+ {
+ Result = HitObject.HitWindows.ResultFor(offset) ?? HitResult.Miss,
+ HasBroken = holdNote.hasBroken
+ });
}
- protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
+ public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down
+
+ public override bool OnReleased(ManiaAction action)
{
// Make sure that the user started holding the key during the hold note
if (!holdNote.holdStartTime.HasValue)
return false;
- if (Judgement.Result != HitResult.None)
- return false;
-
- if (args.Key != Key)
+ if (action != Action)
return false;
UpdateJudgement(true);
@@ -227,12 +267,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
// Handled by the hold note, which will set holding = false
return false;
}
-
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
- {
- // Tail doesn't handle key down
- return false;
- }
}
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
index 39abbb6b3d..324f4e4e99 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
@@ -23,11 +23,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
///
public Func HoldStartTime;
- ///
- /// References whether the user is currently holding the hold note.
- ///
- public Func IsHolding;
-
private readonly Container glowContainer;
public DrawableHoldNoteTick(HoldNoteTick hitObject)
@@ -36,9 +31,15 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
+ Y = (float)HitObject.StartTime;
+
RelativeSizeAxes = Axes.X;
Size = new Vector2(1);
+ // Life time managed by the parent DrawableHoldNote
+ LifetimeStart = double.MinValue;
+ LifetimeEnd = double.MaxValue;
+
Children = new[]
{
glowContainer = new CircularContainer
@@ -58,9 +59,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
};
-
- // Set the default glow
- AccentColour = Color4.White;
}
public override Color4 AccentColour
@@ -80,9 +78,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
- protected override ManiaJudgement CreateJudgement() => new HoldNoteTickJudgement();
-
- protected override void CheckJudgement(bool userTriggered)
+ protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered)
return;
@@ -93,8 +89,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (HoldStartTime?.Invoke() > HitObject.StartTime)
return;
- Judgement.ManiaResult = ManiaHitResult.Perfect;
- Judgement.Result = HitResult.Hit;
+ AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
}
protected override void UpdateState(ArmedState state)
@@ -109,10 +104,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected override void Update()
{
- if (Judgement.Result != HitResult.None)
+ if (AllJudged)
return;
- if (IsHolding?.Invoke() != true)
+ if (HoldStartTime?.Invoke() == null)
return;
UpdateJudgement(true);
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
index 10dc607ec3..6354c6ff77 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
@@ -1,31 +1,30 @@
// 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 OpenTK.Graphics;
-using OpenTK.Input;
-using osu.Framework.Configuration;
-using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
- public abstract class DrawableManiaHitObject : DrawableScrollingHitObject
+ public abstract class DrawableManiaHitObject : DrawableScrollingHitObject
where TObject : ManiaHitObject
{
///
/// The key that will trigger input for this hit object.
///
- protected Bindable Key { get; private set; } = new Bindable();
+ protected ManiaAction Action { get; }
public new TObject HitObject;
- protected DrawableManiaHitObject(TObject hitObject, Bindable key = null)
+ protected DrawableManiaHitObject(TObject hitObject, ManiaAction? action = null)
: base(hitObject)
{
+ RelativePositionAxes = Axes.Y;
HitObject = hitObject;
- if (key != null)
- Key.BindTo(key);
+ if (action != null)
+ Action = action.Value;
}
public override Color4 AccentColour
@@ -38,7 +37,5 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
base.AccentColour = value;
}
}
-
- protected override ManiaJudgement CreateJudgement() => new ManiaJudgement();
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
index 9322fed3eb..537246509b 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
@@ -3,10 +3,8 @@
using System;
using OpenTK.Graphics;
-using OpenTK.Input;
-using osu.Framework.Configuration;
using osu.Framework.Graphics;
-using osu.Framework.Input;
+using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Objects.Drawables;
@@ -16,21 +14,33 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
///
/// Visualises a hit object.
///
- public class DrawableNote : DrawableManiaHitObject
+ public class DrawableNote : DrawableManiaHitObject, IKeyBindingHandler
{
+ protected readonly GlowPiece GlowPiece;
+
+ private readonly LaneGlowPiece laneGlowPiece;
private readonly NotePiece headPiece;
- public DrawableNote(Note hitObject, Bindable key = null)
- : base(hitObject, key)
+ public DrawableNote(Note hitObject, ManiaAction action)
+ : base(hitObject, action)
{
RelativeSizeAxes = Axes.X;
- Height = 100;
+ AutoSizeAxes = Axes.Y;
- Add(headPiece = new NotePiece
+ Children = new Drawable[]
{
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre
- });
+ laneGlowPiece = new LaneGlowPiece
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre
+ },
+ GlowPiece = new GlowPiece(),
+ headPiece = new NotePiece
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre
+ }
+ };
}
public override Color4 AccentColour
@@ -42,57 +52,41 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return;
base.AccentColour = value;
+ laneGlowPiece.AccentColour = value;
+ GlowPiece.AccentColour = value;
headPiece.AccentColour = value;
}
}
- protected override void CheckJudgement(bool userTriggered)
+ protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
- if (Judgement.TimeOffset > HitObject.HitWindows.Bad / 2)
- Judgement.Result = HitResult.Miss;
+ if (timeOffset > HitObject.HitWindows.Bad / 2)
+ AddJudgement(new ManiaJudgement { Result = HitResult.Miss });
return;
}
- double offset = Math.Abs(Judgement.TimeOffset);
+ double offset = Math.Abs(timeOffset);
if (offset > HitObject.HitWindows.Miss / 2)
return;
- ManiaHitResult? tmpResult = HitObject.HitWindows.ResultFor(offset);
-
- if (tmpResult.HasValue)
- {
- Judgement.Result = HitResult.Hit;
- Judgement.ManiaResult = tmpResult.Value;
- }
- else
- Judgement.Result = HitResult.Miss;
+ AddJudgement(new ManiaJudgement { Result = HitObject.HitWindows.ResultFor(offset) ?? HitResult.Miss });
}
protected override void UpdateState(ArmedState state)
{
- switch (State)
- {
- case ArmedState.Hit:
- Colour = Color4.Green;
- break;
- }
}
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
+ public virtual bool OnPressed(ManiaAction action)
{
- if (Judgement.Result != HitResult.None)
- return false;
-
- if (args.Key != Key)
- return false;
-
- if (args.Repeat)
+ if (action != Action)
return false;
return UpdateJudgement(true);
}
+
+ public virtual bool OnReleased(ManiaAction action) => false;
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
index 04e8df4ae2..fe40532e42 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
@@ -1,7 +1,10 @@
// 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.Caching;
using OpenTK.Graphics;
+using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -14,22 +17,61 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
///
internal class BodyPiece : Container, IHasAccentColour
{
- private readonly Box box;
+ private readonly Container subtractionLayer;
+
+ private readonly Drawable background;
+ private readonly BufferedContainer foreground;
+ private readonly BufferedContainer subtractionContainer;
public BodyPiece()
{
- RelativeSizeAxes = Axes.Both;
+ Blending = BlendingMode.Additive;
Children = new[]
{
- box = new Box
+ background = new Box { RelativeSizeAxes = Axes.Both },
+ foreground = new BufferedContainer
{
RelativeSizeAxes = Axes.Both,
- Alpha = 0.3f
+ CacheDrawnFrameBuffer = true,
+ Children = new Drawable[]
+ {
+ new Box { RelativeSizeAxes = Axes.Both },
+ subtractionContainer = new BufferedContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ // This is needed because we're blending with another object
+ BackgroundColour = Color4.White.Opacity(0),
+ CacheDrawnFrameBuffer = true,
+ // The 'hole' is achieved by subtracting the result of this container with the parent
+ Blending = new BlendingParameters { AlphaEquation = BlendingEquation.ReverseSubtract },
+ Child = subtractionLayer = new CircularContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ // Height computed in Update
+ Width = 1,
+ Masking = true,
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0,
+ AlwaysPresent = true
+ }
+ }
+ }
+ }
}
};
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ updateAccentColour();
+ }
+
private Color4 accentColour;
public Color4 AccentColour
{
@@ -40,8 +82,51 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
return;
accentColour = value;
- box.Colour = accentColour;
+ updateAccentColour();
}
}
+
+ private Cached subtractionCache = new Cached();
+
+ public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
+ {
+ if ((invalidation & Invalidation.DrawSize) > 0)
+ subtractionCache.Invalidate();
+
+ return base.Invalidate(invalidation, source, shallPropagate);
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (!subtractionCache.IsValid)
+ {
+ subtractionLayer.Width = 5;
+ subtractionLayer.Height = Math.Max(0, DrawHeight - DrawWidth);
+ subtractionLayer.EdgeEffect = new EdgeEffectParameters
+ {
+ Colour = Color4.White,
+ Type = EdgeEffectType.Glow,
+ Radius = DrawWidth
+ };
+
+ foreground.ForceRedraw();
+ subtractionContainer.ForceRedraw();
+
+ subtractionCache.Validate();
+ }
+ }
+
+ private void updateAccentColour()
+ {
+ if (!IsLoaded)
+ return;
+
+ foreground.Colour = AccentColour.Opacity(0.4f);
+ background.Colour = AccentColour.Opacity(0.2f);
+
+ subtractionCache.Invalidate();
+ }
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs
new file mode 100644
index 0000000000..6f022f452f
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs
@@ -0,0 +1,65 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using OpenTK.Graphics;
+
+namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
+{
+ public class GlowPiece : CompositeDrawable, IHasAccentColour
+ {
+ private const float glow_alpha = 0.7f;
+ private const float glow_radius = 5;
+
+ public GlowPiece()
+ {
+ RelativeSizeAxes = Axes.Both;
+ Masking = true;
+
+ InternalChild = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0,
+ AlwaysPresent = true
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ updateGlow();
+ }
+
+ private Color4 accentColour;
+ public Color4 AccentColour
+ {
+ get { return accentColour; }
+ set
+ {
+ if (accentColour == value)
+ return;
+ accentColour = value;
+
+ updateGlow();
+ }
+ }
+
+ private void updateGlow()
+ {
+ if (!IsLoaded)
+ return;
+
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Glow,
+ Colour = AccentColour.Opacity(glow_alpha),
+ Radius = glow_radius,
+ Hollow = true
+ };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/LaneGlowPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/LaneGlowPiece.cs
new file mode 100644
index 0000000000..6d4ac2fb61
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/LaneGlowPiece.cs
@@ -0,0 +1,85 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using OpenTK.Graphics;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+
+namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
+{
+ public class LaneGlowPiece : CompositeDrawable, IHasAccentColour
+ {
+ private const float total_height = 100;
+ private const float glow_height = 50;
+ private const float glow_alpha = 0.4f;
+ private const float edge_alpha = 0.3f;
+
+ public LaneGlowPiece()
+ {
+ BypassAutoSizeAxes = Axes.Both;
+ RelativeSizeAxes = Axes.X;
+ Height = total_height;
+
+ InternalChildren = new[]
+ {
+ new Container
+ {
+ Name = "Left edge",
+ RelativeSizeAxes = Axes.Y,
+ Width = 1,
+ Children = createGradient(edge_alpha)
+ },
+ new Container
+ {
+ Name = "Right edge",
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ RelativeSizeAxes = Axes.Y,
+ Width = 1,
+ Children = createGradient(edge_alpha)
+ },
+ new Container
+ {
+ Name = "Glow",
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.X,
+ Height = glow_height,
+ Children = createGradient(glow_alpha)
+ }
+ };
+ }
+
+ private Drawable[] createGradient(float alpha) => new Drawable[]
+ {
+ new Box
+ {
+ Name = "Top",
+ RelativeSizeAxes = Axes.Both,
+ Height = 0.5f,
+ Blending = BlendingMode.Additive,
+ Colour = ColourInfo.GradientVertical(Color4.Transparent, Color4.White.Opacity(alpha))
+ },
+ new Box
+ {
+ Name = "Bottom",
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ RelativeSizeAxes = Axes.Both,
+ Height = 0.5f,
+ Blending = BlendingMode.Additive,
+ Colour = ColourInfo.GradientVertical(Color4.White.Opacity(alpha), Color4.Transparent)
+ }
+ };
+
+ public Color4 AccentColour
+ {
+ get { return Colour; }
+ set { Colour = value; }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
index c3a29b39a8..fc1331551e 100644
--- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
@@ -37,6 +37,17 @@ namespace osu.Game.Rulesets.Mania.Objects
}
}
+ public override int Column
+ {
+ get { return base.Column; }
+ set
+ {
+ base.Column = value;
+ Head.Column = value;
+ Tail.Column = value;
+ }
+ }
+
///
/// The head note of the hold.
///
@@ -80,7 +91,8 @@ namespace osu.Game.Rulesets.Mania.Objects
{
ret.Add(new HoldNoteTick
{
- StartTime = t
+ StartTime = t,
+ Column = Column
});
}
diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs
index 93aaa94f45..7beb21f9e3 100644
--- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs
+++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs
@@ -8,6 +8,6 @@ namespace osu.Game.Rulesets.Mania.Objects
{
public abstract class ManiaHitObject : HitObject, IHasColumn
{
- public int Column { get; set; }
+ public virtual int Column { get; set; }
}
}
diff --git a/osu.Game.Rulesets.Mania/OpenTK.dll.config b/osu.Game.Rulesets.Mania/OpenTK.dll.config
index 627e9f6009..5620e3d9e2 100644
--- a/osu.Game.Rulesets.Mania/OpenTK.dll.config
+++ b/osu.Game.Rulesets.Mania/OpenTK.dll.config
@@ -1,7 +1,3 @@
-
diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
new file mode 100644
index 0000000000..64982532a7
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
@@ -0,0 +1,133 @@
+// 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.Game.Beatmaps;
+using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Replays;
+using osu.Game.Users;
+
+namespace osu.Game.Rulesets.Mania.Replays
+{
+ internal class ManiaAutoGenerator : AutoGenerator
+ {
+ private const double release_delay = 20;
+
+ private readonly int availableColumns;
+
+ public ManiaAutoGenerator(Beatmap beatmap, int availableColumns)
+ : base(beatmap)
+ {
+ this.availableColumns = availableColumns;
+
+ Replay = new Replay { User = new User { Username = @"Autoplay" } };
+ }
+
+ protected Replay Replay;
+
+ public override Replay Generate()
+ {
+ // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
+ Replay.Frames.Add(new ReplayFrame(-100000, null, null, ReplayButtonState.None));
+
+ double[] holdEndTimes = new double[availableColumns];
+ for (int i = 0; i < availableColumns; i++)
+ holdEndTimes[i] = double.NegativeInfinity;
+
+ // Notes are handled row-by-row
+ foreach (var objGroup in Beatmap.HitObjects.GroupBy(h => h.StartTime))
+ {
+ double groupTime = objGroup.Key;
+
+ int activeColumns = 0;
+
+ // Get the previously held-down active columns
+ for (int i = 0; i < availableColumns; i++)
+ {
+ if (holdEndTimes[i] > groupTime)
+ activeColumns |= 1 << i;
+ }
+
+ // Add on the group columns, keeping track of the held notes for the next rows
+ foreach (var obj in objGroup)
+ {
+ var holdNote = obj as HoldNote;
+ if (holdNote != null)
+ holdEndTimes[obj.Column] = Math.Max(holdEndTimes[obj.Column], holdNote.EndTime);
+
+ activeColumns |= 1 << obj.Column;
+ }
+
+ Replay.Frames.Add(new ReplayFrame(groupTime, activeColumns, null, ReplayButtonState.None));
+
+ // Add the release frames. We can't do this with the loop above because we need activeColumns to be fully populated
+ foreach (var obj in objGroup.GroupBy(h => (h as IHasEndTime)?.EndTime ?? h.StartTime + release_delay).OrderBy(h => h.Key))
+ {
+ var groupEndTime = obj.Key;
+
+ int activeColumnsAtEnd = 0;
+ for (int i = 0; i < availableColumns; i++)
+ {
+ if (holdEndTimes[i] > groupEndTime)
+ activeColumnsAtEnd |= 1 << i;
+ }
+
+ Replay.Frames.Add(new ReplayFrame(groupEndTime, activeColumnsAtEnd, 0, ReplayButtonState.None));
+ }
+ }
+
+ Replay.Frames = Replay.Frames
+ // Pick the maximum activeColumns for all frames at the same time
+ .GroupBy(f => f.Time)
+ .Select(g => new ReplayFrame(g.First().Time, maxMouseX(g), 0, ReplayButtonState.None))
+ // The addition of release frames above maybe result in unordered frames, but we need them ordered
+ .OrderBy(f => f.Time)
+ .ToList();
+
+ return Replay;
+ }
+
+ ///
+ /// Finds the maximum by count of bits from a grouping of s.
+ ///
+ /// The grouping to search.
+ /// The maximum by count of bits.
+ private float maxMouseX(IGrouping group)
+ {
+ int currentCount = -1;
+ int currentMax = 0;
+
+ foreach (var val in group)
+ {
+ int newCount = countBits((int)(val.MouseX ?? 0));
+ if (newCount > currentCount)
+ {
+ currentCount = newCount;
+ currentMax = (int)(val.MouseX ?? 0);
+ }
+ }
+
+ return currentMax;
+ }
+
+ ///
+ /// Counts the number of bits set in a value.
+ ///
+ /// The value to count.
+ /// The number of set bits.
+ private int countBits(int value)
+ {
+ int count = 0;
+ while (value > 0)
+ {
+ if ((value & 1) > 0)
+ count++;
+ value >>= 1;
+ }
+
+ return count;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs b/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs
new file mode 100644
index 0000000000..e352997f2c
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs
@@ -0,0 +1,35 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using osu.Framework.Input;
+using osu.Game.Rulesets.Replays;
+
+namespace osu.Game.Rulesets.Mania.Replays
+{
+ internal class ManiaFramedReplayInputHandler : FramedReplayInputHandler
+ {
+ public ManiaFramedReplayInputHandler(Replay replay)
+ : base(replay)
+ {
+ }
+
+ public override List GetPendingStates()
+ {
+ var actions = new List();
+
+ int activeColumns = (int)(CurrentFrame.MouseX ?? 0);
+
+ int counter = 0;
+ while (activeColumns > 0)
+ {
+ if ((activeColumns & 1) > 0)
+ actions.Add(ManiaAction.Key1 + counter);
+ counter++;
+ activeColumns >>= 1;
+ }
+
+ return new List { new ReplayState { PressedActions = actions } };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
index 63b443319f..a200ba31e2 100644
--- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
+++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
@@ -1,9 +1,9 @@
// 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.Game.Beatmaps;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects.Drawables;
@@ -12,34 +12,8 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Scoring
{
- internal class ManiaScoreProcessor : ScoreProcessor
+ internal class ManiaScoreProcessor : ScoreProcessor
{
- ///
- /// The maximum score achievable.
- /// Does _not_ include bonus score - for bonus score see .
- ///
- private const int max_score = 1000000;
-
- ///
- /// The amount of the score attributed to combo.
- ///
- private const double combo_portion_max = max_score * 0.2;
-
- ///
- /// The amount of the score attributed to accuracy.
- ///
- private const double accuracy_portion_max = max_score * 0.8;
-
- ///
- /// The factor used to determine relevance of combos.
- ///
- private const double combo_base = 4;
-
- ///
- /// The combo value at which hit objects result in the max score possible.
- ///
- private const int combo_relevance_cap = 400;
-
///
/// The hit HP multiplier at OD = 0.
///
@@ -115,52 +89,16 @@ namespace osu.Game.Rulesets.Mania.Scoring
///
private double hpMultiplier = 1;
- ///
- /// The cumulative combo portion of the score.
- ///
- private double comboScore => combo_portion_max * comboPortion / maxComboPortion;
-
- ///
- /// The cumulative accuracy portion of the score.
- ///
- private double accuracyScore => accuracy_portion_max * Math.Pow(Accuracy, 4) * totalHits / maxTotalHits;
-
- ///
- /// The cumulative bonus score.
- /// This is added on top of , thus the total score can exceed .
- ///
- private double bonusScore;
-
- ///
- /// The achieved by a perfect playthrough.
- ///
- private double maxComboPortion;
-
- ///
- /// The portion of the score dedicated to combo.
- ///
- private double comboPortion;
-
- ///
- /// The achieved by a perfect playthrough.
- ///
- private int maxTotalHits;
-
- ///
- /// The total hits.
- ///
- private int totalHits;
-
public ManiaScoreProcessor()
{
}
- public ManiaScoreProcessor(RulesetContainer rulesetContainer)
+ public ManiaScoreProcessor(RulesetContainer rulesetContainer)
: base(rulesetContainer)
{
}
- protected override void ComputeTargets(Beatmap beatmap)
+ protected override void SimulateAutoplay(Beatmap beatmap)
{
BeatmapDifficulty difficulty = beatmap.BeatmapInfo.Difficulty;
hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
@@ -172,40 +110,18 @@ namespace osu.Game.Rulesets.Mania.Scoring
{
var holdNote = obj as HoldNote;
- if (obj is Note)
- {
- AddJudgement(new ManiaJudgement
- {
- Result = HitResult.Hit,
- ManiaResult = ManiaHitResult.Perfect
- });
- }
- else if (holdNote != null)
+ if (holdNote != null)
{
// Head
- AddJudgement(new ManiaJudgement
- {
- Result = HitResult.Hit,
- ManiaResult = ManiaJudgement.MAX_HIT_RESULT
- });
+ AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
// Ticks
int tickCount = holdNote.Ticks.Count();
for (int i = 0; i < tickCount; i++)
- {
- AddJudgement(new HoldNoteTickJudgement
- {
- Result = HitResult.Hit,
- ManiaResult = ManiaJudgement.MAX_HIT_RESULT,
- });
- }
-
- AddJudgement(new HoldNoteTailJudgement
- {
- Result = HitResult.Hit,
- ManiaResult = ManiaJudgement.MAX_HIT_RESULT
- });
+ AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
}
+
+ AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
}
if (!HasFailed)
@@ -214,81 +130,45 @@ namespace osu.Game.Rulesets.Mania.Scoring
hpMultiplier *= 1.01;
hpMissMultiplier *= 0.98;
- Reset();
+ Reset(false);
}
-
- maxTotalHits = totalHits;
- maxComboPortion = comboPortion;
}
- protected override void OnNewJudgement(ManiaJudgement judgement)
+ protected override void OnNewJudgement(Judgement judgement)
{
+ base.OnNewJudgement(judgement);
+
bool isTick = judgement is HoldNoteTickJudgement;
- if (!isTick)
- totalHits++;
-
- switch (judgement.Result)
+ if (isTick)
{
- case HitResult.Miss:
- Health.Value += hpMissMultiplier * hp_increase_miss;
- break;
- case HitResult.Hit:
- if (isTick)
- {
- Health.Value += hpMultiplier * hp_increase_tick;
- bonusScore += judgement.ResultValueForScore;
- }
- else
- {
- switch (judgement.ManiaResult)
- {
- case ManiaHitResult.Bad:
- Health.Value += hpMultiplier * hp_increase_bad;
- break;
- case ManiaHitResult.Ok:
- Health.Value += hpMultiplier * hp_increase_ok;
- break;
- case ManiaHitResult.Good:
- Health.Value += hpMultiplier * hp_increase_good;
- break;
- case ManiaHitResult.Great:
- Health.Value += hpMultiplier * hp_increase_great;
- break;
- case ManiaHitResult.Perfect:
- Health.Value += hpMultiplier * hp_increase_perfect;
- break;
- }
-
- // A factor that is applied to make higher combos more relevant
- double comboRelevance = Math.Min(Math.Max(0.5, Math.Log(Combo.Value, combo_base)), Math.Log(combo_relevance_cap, combo_base));
- comboPortion += judgement.ResultValueForScore * comboRelevance;
- }
- break;
+ if (judgement.IsHit)
+ Health.Value += hpMultiplier * hp_increase_tick;
}
-
- int scoreForAccuracy = 0;
- int maxScoreForAccuracy = 0;
-
- foreach (var j in Judgements)
+ else
{
- scoreForAccuracy += j.ResultValueForAccuracy;
- maxScoreForAccuracy += j.MaxResultValueForAccuracy;
+ switch (judgement.Result)
+ {
+ case HitResult.Miss:
+ Health.Value += hpMissMultiplier * hp_increase_miss;
+ break;
+ case HitResult.Meh:
+ Health.Value += hpMultiplier * hp_increase_bad;
+ break;
+ case HitResult.Ok:
+ Health.Value += hpMultiplier * hp_increase_ok;
+ break;
+ case HitResult.Good:
+ Health.Value += hpMultiplier * hp_increase_good;
+ break;
+ case HitResult.Great:
+ Health.Value += hpMultiplier * hp_increase_great;
+ break;
+ case HitResult.Perfect:
+ Health.Value += hpMultiplier * hp_increase_perfect;
+ break;
+ }
}
-
- Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy;
- TotalScore.Value = comboScore + accuracyScore + bonusScore;
- }
-
- protected override void Reset()
- {
- base.Reset();
-
- Health.Value = 1;
-
- bonusScore = 0;
- comboPortion = 0;
- totalHits = 0;
}
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index 9fbc9ba5e7..2d553f8639 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -3,24 +3,21 @@
using OpenTK;
using OpenTK.Graphics;
-using OpenTK.Input;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Colour;
-using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using System;
-using osu.Framework.Configuration;
+using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.UI;
-using osu.Game.Rulesets.Mania.Objects;
-using osu.Game.Rulesets.Mania.Judgements;
+using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Mania.UI
{
- public class Column : ScrollingPlayfield, IHasAccentColour
+ public class Column : ScrollingPlayfield, IHasAccentColour
{
private const float key_icon_size = 10;
private const float key_icon_corner_radius = 3;
@@ -32,18 +29,21 @@ namespace osu.Game.Rulesets.Mania.UI
private const float column_width = 45;
private const float special_column_width = 70;
- ///
- /// The key that will trigger input actions for this column and hit objects contained inside it.
- ///
- public Bindable Key = new Bindable();
+ public ManiaAction Action;
private readonly Box background;
private readonly Container hitTargetBar;
private readonly Container keyIcon;
+ internal readonly Container TopLevelContainer;
+ private readonly Container explosionContainer;
+
protected override Container Content => content;
private readonly Container content;
+ private const float opacity_released = 0.1f;
+ private const float opacity_pressed = 0.25f;
+
public Column()
: base(Axes.Y)
{
@@ -53,9 +53,9 @@ namespace osu.Game.Rulesets.Mania.UI
{
background = new Box
{
- Name = "Foreground",
+ Name = "Background",
RelativeSizeAxes = Axes.Both,
- Alpha = 0.2f
+ Alpha = opacity_released
},
new Container
{
@@ -101,8 +101,13 @@ namespace osu.Game.Rulesets.Mania.UI
// For column lighting, we need to capture input events before the notes
new InputTarget
{
- KeyDown = onKeyDown,
- KeyUp = onKeyUp
+ Pressed = onPressed,
+ Released = onReleased
+ },
+ explosionContainer = new Container
+ {
+ Name = "Hit explosions",
+ RelativeSizeAxes = Axes.Both
}
}
},
@@ -141,8 +146,11 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
}
- }
+ },
+ TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
};
+
+ TopLevelContainer.Add(explosionContainer.CreateProxy());
}
public override Axes RelativeSizeAxes => Axes.Y;
@@ -193,31 +201,38 @@ namespace osu.Game.Rulesets.Mania.UI
/// Adds a DrawableHitObject to this Playfield.
///
/// The DrawableHitObject to add.
- public override void Add(DrawableHitObject hitObject)
+ public override void Add(DrawableHitObject hitObject)
{
+ hitObject.Depth = (float)hitObject.HitObject.StartTime;
+
hitObject.AccentColour = AccentColour;
HitObjects.Add(hitObject);
}
- private bool onKeyDown(InputState state, KeyDownEventArgs args)
+ public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
- if (args.Repeat)
- return false;
+ if (!judgement.IsHit)
+ return;
- if (args.Key == Key)
+ explosionContainer.Add(new HitExplosion(judgedObject));
+ }
+
+ private bool onPressed(ManiaAction action)
+ {
+ if (action == Action)
{
- background.FadeTo(background.Alpha + 0.2f, 50, Easing.OutQuint);
+ background.FadeTo(opacity_pressed, 50, Easing.OutQuint);
keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint);
}
return false;
}
- private bool onKeyUp(InputState state, KeyUpEventArgs args)
+ private bool onReleased(ManiaAction action)
{
- if (args.Key == Key)
+ if (action == Action)
{
- background.FadeTo(0.2f, 800, Easing.OutQuart);
+ background.FadeTo(opacity_released, 800, Easing.OutQuart);
keyIcon.ScaleTo(1f, 400, Easing.OutQuart);
}
@@ -227,10 +242,10 @@ namespace osu.Game.Rulesets.Mania.UI
///
/// This is a simple container which delegates various input events that have to be captured before the notes.
///
- private class InputTarget : Container
+ private class InputTarget : Container, IKeyBindingHandler
{
- public Func KeyDown;
- public Func KeyUp;
+ public Func Pressed;
+ public Func Released;
public InputTarget()
{
@@ -239,8 +254,8 @@ namespace osu.Game.Rulesets.Mania.UI
Alpha = 0;
}
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => KeyDown?.Invoke(state, args) ?? false;
- protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => KeyUp?.Invoke(state, args) ?? false;
+ public bool OnPressed(ManiaAction action) => Pressed?.Invoke(action) ?? false;
+ public bool OnReleased(ManiaAction action) => Released?.Invoke(action) ?? false;
}
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
new file mode 100644
index 0000000000..08478cef33
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
@@ -0,0 +1,34 @@
+// 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.Rulesets.Judgements;
+
+namespace osu.Game.Rulesets.Mania.UI
+{
+ internal class DrawableManiaJudgement : DrawableJudgement
+ {
+ public DrawableManiaJudgement(Judgement judgement)
+ : base(judgement)
+ {
+ JudgementText.TextSize = 25;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ this.FadeInFromZero(50, Easing.OutQuint);
+
+ if (Judgement.IsHit)
+ {
+ this.ScaleTo(0.8f);
+ this.ScaleTo(1, 250, Easing.OutElastic);
+
+ this.Delay(50).FadeOut(200).ScaleTo(0.75f, 250);
+ }
+
+ Expire();
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs
new file mode 100644
index 0000000000..8164adcebd
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs
@@ -0,0 +1,64 @@
+// 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.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Mania.UI
+{
+ internal class HitExplosion : CompositeDrawable
+ {
+ private readonly Box inner;
+
+ public HitExplosion(DrawableHitObject judgedObject)
+ {
+ bool isTick = judgedObject is DrawableHoldNoteTick;
+
+ Anchor = Anchor.TopCentre;
+ Origin = Anchor.Centre;
+
+ RelativeSizeAxes = Axes.Both;
+ Size = new Vector2(isTick ? 0.5f : 1);
+ FillMode = FillMode.Fit;
+
+ Blending = BlendingMode.Additive;
+
+ Color4 accent = isTick ? Color4.White : judgedObject.AccentColour;
+
+ InternalChild = new CircularContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ BorderThickness = 1,
+ BorderColour = accent,
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Glow,
+ Colour = accent,
+ Radius = 10,
+ Hollow = true
+ },
+ Child = inner = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = accent,
+ Alpha = 1,
+ AlwaysPresent = true,
+ }
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ this.ScaleTo(2f, 600, Easing.OutQuint).FadeOut(500).Expire();
+ inner.FadeOut(250);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
index b836b54fa8..6c164a34f0 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
@@ -6,30 +6,24 @@ using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.UI;
using OpenTK;
using OpenTK.Graphics;
-using osu.Game.Rulesets.Mania.Judgements;
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;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Mania.UI
{
- public class ManiaPlayfield : ScrollingPlayfield
+ public class ManiaPlayfield : ScrollingPlayfield
{
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.
@@ -45,6 +39,11 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
+ ///
+ /// Whether this playfield should be inverted. This flips everything inside the playfield.
+ ///
+ public readonly Bindable Inverted = new Bindable(true);
+
private readonly FlowContainer columns;
public IEnumerable Columns => columns.Children;
@@ -54,6 +53,8 @@ namespace osu.Game.Rulesets.Mania.UI
private List normalColumnColours = new List();
private Color4 specialColumnColour;
+ private readonly Container judgements;
+
private readonly int columnCount;
public ManiaPlayfield(int columnCount)
@@ -64,21 +65,23 @@ namespace osu.Game.Rulesets.Mania.UI
if (columnCount <= 0)
throw new ArgumentException("Can't have zero or fewer columns.");
+ Inverted.Value = true;
+
+ Container topLevelContainer;
InternalChildren = new Drawable[]
{
new Container
{
+ Name = "Playfield elements",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
- RelativeSizeAxes = Axes.Both,
- Masking = true,
+ RelativeSizeAxes = Axes.Y,
+ AutoSizeAxes = Axes.X,
Children = new Drawable[]
{
new Container
{
- Name = "Masked elements",
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
+ Name = "Columns mask",
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Masking = true,
@@ -86,6 +89,7 @@ namespace osu.Game.Rulesets.Mania.UI
{
new Box
{
+ Name = "Background",
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
},
@@ -97,39 +101,63 @@ namespace osu.Game.Rulesets.Mania.UI
Direction = FillDirection.Horizontal,
Padding = new MarginPadding { Left = 1, Right = 1 },
Spacing = new Vector2(1, 0)
- }
+ },
}
},
new Container
{
+ Name = "Barlines mask",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Top = HIT_TARGET_POSITION },
- Children = new[]
+ RelativeSizeAxes = Axes.Y,
+ Width = 1366, // Bar lines should only be masked on the vertical axis
+ BypassAutoSizeAxes = Axes.Both,
+ Masking = true,
+ Child = content = new Container
{
- content = new Container
- {
- Name = "Bar lines",
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- RelativeSizeAxes = Axes.Y,
- // Width is set in the Update method
- }
+ Name = "Bar lines",
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ RelativeSizeAxes = Axes.Y,
+ Padding = new MarginPadding { Top = HIT_TARGET_POSITION }
}
- }
+ },
+ judgements = new Container
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.Centre,
+ AutoSizeAxes = Axes.Both,
+ Y = HIT_TARGET_POSITION + 150,
+ BypassAutoSizeAxes = Axes.Both
+ },
+ topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
}
}
};
+ var currentAction = ManiaAction.Key1;
for (int i = 0; i < columnCount; i++)
{
var c = new Column();
c.VisibleTimeRange.BindTo(VisibleTimeRange);
+ c.IsSpecial = isSpecialColumn(i);
+ c.Action = c.IsSpecial ? ManiaAction.Special : currentAction++;
+
+ topLevelContainer.Add(c.TopLevelContainer.CreateProxy());
+
columns.Add(c);
AddNested(c);
}
+
+ Inverted.ValueChanged += invertedChanged;
+ Inverted.TriggerChange();
+ }
+
+ private void invertedChanged(bool newValue)
+ {
+ Scale = new Vector2(1, newValue ? -1 : 1);
+ judgements.Scale = Scale;
}
[BackgroundDependencyLoader]
@@ -144,15 +172,11 @@ namespace osu.Game.Rulesets.Mania.UI
specialColumnColour = colours.BlueDark;
// Set the special column + colour + key
- for (int i = 0; i < columnCount; i++)
+ foreach (var column in Columns)
{
- Column column = Columns.ElementAt(i);
- column.IsSpecial = isSpecialColumn(i);
-
if (!column.IsSpecial)
continue;
- column.Key.Value = Key.Space;
column.AccentColour = specialColumnColour;
}
@@ -166,21 +190,19 @@ namespace osu.Game.Rulesets.Mania.UI
nonSpecialColumns[i].AccentColour = colour;
nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour;
}
+ }
- // We'll set the keys for non-special columns in another separate loop because it's not mirrored like the above colours
- // Todo: This needs to go when we get to bindings and use Button1, ..., ButtonN instead
- for (int i = 0; i < nonSpecialColumns.Count; i++)
+ public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
+ {
+ var maniaObject = (ManiaHitObject)judgedObject.HitObject;
+ columns[maniaObject.Column].OnJudgement(judgedObject, judgement);
+
+ judgements.Clear();
+ judgements.Add(new DrawableManiaJudgement(judgement)
{
- Column column = nonSpecialColumns[i];
-
- int keyOffset = default_keys.Length / 2 - nonSpecialColumns.Count / 2 + i;
- if (keyOffset >= 0 && keyOffset < default_keys.Length)
- column.Key.Value = default_keys[keyOffset];
- else
- // There is no default key defined for this column. Let's set this to Unknown for now
- // however note that this will be gone after bindings are in place
- column.Key.Value = Key.Unknown;
- }
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ });
}
///
@@ -202,7 +224,8 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
- public override void Add(DrawableHitObject h) => Columns.ElementAt(h.HitObject.Column).Add(h);
+ public override void Add(DrawableHitObject h) => Columns.ElementAt(((ManiaHitObject)h.HitObject).Column).Add(h);
+
public void Add(DrawableBarLine barline) => HitObjects.Add(barline);
protected override void Update()
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
index 7f6b19f3bd..e9c4895284 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
@@ -5,36 +5,37 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenTK;
-using OpenTK.Input;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
+using osu.Framework.Input;
using osu.Framework.Lists;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
-using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mania.Timing;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Timing;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.UI
{
- public class ManiaRulesetContainer : ScrollingRulesetContainer
+ public class ManiaRulesetContainer : ScrollingRulesetContainer
{
///
- /// Preferred column count. This will only have an effect during the initialization of the play field.
+ /// The number of columns which the should display, and which
+ /// the beatmap converter will attempt to convert beatmaps to use.
///
- public int PreferredColumns;
+ public int AvailableColumns { get; private set; }
public IEnumerable BarLines;
@@ -75,36 +76,47 @@ namespace osu.Game.Rulesets.Mania.UI
BarLines.ForEach(Playfield.Add);
}
- protected override void ApplyBeatmap()
- {
- base.ApplyBeatmap();
-
- PreferredColumns = (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize));
- }
-
- protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(PreferredColumns)
+ protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(AvailableColumns)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- // Invert by default for now (should be moved to config/skin later)
- Scale = new Vector2(1, -1)
};
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
- protected override BeatmapConverter CreateBeatmapConverter() => new ManiaBeatmapConverter();
+ public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, AvailableColumns);
- protected override DrawableHitObject GetVisualRepresentation(ManiaHitObject h)
+ protected override BeatmapConverter CreateBeatmapConverter()
{
- Bindable key = Playfield.Columns.ElementAt(h.Column).Key;
+ if (IsForCurrentRuleset)
+ AvailableColumns = (int)Math.Max(1, Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize));
+ else
+ {
+ float percentSliderOrSpinner = (float)WorkingBeatmap.Beatmap.HitObjects.Count(h => h is IHasEndTime) / WorkingBeatmap.Beatmap.HitObjects.Count;
+ if (percentSliderOrSpinner < 0.2)
+ AvailableColumns = 7;
+ else if (percentSliderOrSpinner < 0.3 || Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize) >= 5)
+ AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 5 ? 7 : 6;
+ else if (percentSliderOrSpinner > 0.6)
+ AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 4 ? 5 : 4;
+ else
+ AvailableColumns = Math.Max(4, Math.Min((int)Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) + 1, 7));
+ }
+
+ return new ManiaBeatmapConverter(IsForCurrentRuleset, AvailableColumns);
+ }
+
+ protected override DrawableHitObject GetVisualRepresentation(ManiaHitObject h)
+ {
+ ManiaAction action = Playfield.Columns.ElementAt(h.Column).Action;
var holdNote = h as HoldNote;
if (holdNote != null)
- return new DrawableHoldNote(holdNote, key);
+ return new DrawableHoldNote(holdNote, action);
var note = h as Note;
if (note != null)
- return new DrawableNote(note, key);
+ return new DrawableNote(note, action);
return null;
}
@@ -112,5 +124,7 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f);
protected override SpeedAdjustmentContainer CreateSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) => new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Basic);
+
+ protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
}
}
diff --git a/osu.Game.Rulesets.Mania/app.config b/osu.Game.Rulesets.Mania/app.config
new file mode 100644
index 0000000000..faeaf001de
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
index fb0b2d907f..739f1cf48c 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
@@ -34,7 +34,7 @@
- $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
True
@@ -60,7 +60,6 @@
-
@@ -70,8 +69,12 @@
+
+
+
+
@@ -79,9 +82,12 @@
+
+
+
@@ -95,14 +101,6 @@
{C76BF5B3-985E-4D39-95FE-97C9C879B83A}
osu.Framework
-
- {C92A607B-1FDD-4954-9F92-03FF547D9080}
- osu.Game.Rulesets.Osu
-
-
- {F167E17A-7DE6-4AF5-B920-A5112296C695}
- osu.Game.Rulesets.Taiko
-
{0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}
osu.Game
@@ -112,6 +110,7 @@
osu.licenseheader
+
diff --git a/osu.Game.Rulesets.Mania/packages.config b/osu.Game.Rulesets.Mania/packages.config
index 8add43d5d5..0b1838ceee 100644
--- a/osu.Game.Rulesets.Mania/packages.config
+++ b/osu.Game.Rulesets.Mania/packages.config
@@ -1,8 +1,4 @@
-
-
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs
index fce0188cda..621584ce05 100644
--- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs
@@ -1,7 +1,7 @@
// 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.Beatmaps;
using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Objects.Types;
@@ -64,8 +64,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
//We are no longer within stacking range of the next object.
break;
- if (Vector2.Distance(stackBaseObject.Position, objectN.Position) < stack_distance ||
- stackBaseObject is Slider && Vector2.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
+ if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance ||
+ stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
{
stackBaseIndex = n;
@@ -130,14 +130,14 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
* o <- hitCircle has stack of -1
* o <- hitCircle has stack of -2
*/
- if (objectN is Slider && Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
+ if (objectN is Slider && Vector2Extensions.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
{
int offset = objectI.StackHeight - objectN.StackHeight + 1;
for (int j = n + 1; j <= i; j++)
{
//For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above).
OsuHitObject objectJ = beatmap.HitObjects[j];
- if (Vector2.Distance(objectN.EndPosition, objectJ.Position) < stack_distance)
+ if (Vector2Extensions.Distance(objectN.EndPosition, objectJ.Position) < stack_distance)
objectJ.StackHeight -= offset;
}
@@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
break;
}
- if (Vector2.Distance(objectN.Position, objectI.Position) < stack_distance)
+ if (Vector2Extensions.Distance(objectN.Position, objectI.Position) < stack_distance)
{
//Keep processing as if there are no sliders. If we come across a slider, this gets cancelled out.
//NOTE: Sliders with start positions stacking are a special case that is also handled here.
@@ -170,7 +170,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
//We are no longer within stacking range of the previous object.
break;
- if (Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
+ if (Vector2Extensions.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
{
objectN.StackHeight = objectI.StackHeight + 1;
objectI = objectN;
diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs
index d61e179002..28b6a04376 100644
--- a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs
+++ b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs
@@ -4,49 +4,31 @@
using OpenTK;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Objects.Drawables;
-using osu.Framework.Extensions;
+using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Judgements
{
public class OsuJudgement : Judgement
{
+ public override HitResult MaxResult => HitResult.Great;
+
///
/// The positional hit offset.
///
public Vector2 PositionOffset;
- ///
- /// The score the user achieved.
- ///
- public OsuScoreResult Score;
-
- ///
- /// The score which would be achievable on a perfect hit.
- ///
- public OsuScoreResult MaxScore = OsuScoreResult.Hit300;
-
- public override string ResultString => Score.GetDescription();
-
- public override string MaxResultString => MaxScore.GetDescription();
-
- public int ScoreValue => scoreToInt(Score);
-
- public int MaxScoreValue => scoreToInt(MaxScore);
-
- private int scoreToInt(OsuScoreResult result)
+ protected override int NumericResultFor(HitResult result)
{
switch (result)
{
default:
return 0;
- case OsuScoreResult.Hit50:
+ case HitResult.Meh:
return 50;
- case OsuScoreResult.Hit100:
+ case HitResult.Good:
return 100;
- case OsuScoreResult.Hit300:
+ case HitResult.Great:
return 300;
- case OsuScoreResult.SliderTick:
- return 10;
}
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuMod.cs b/osu.Game.Rulesets.Osu/Mods/OsuMod.cs
index 3b0cfc1ef1..2970055bff 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuMod.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuMod.cs
@@ -7,8 +7,13 @@ using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects;
using System;
+using System.Collections.Generic;
using System.Linq;
+using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Scoring;
+using osu.Game.Rulesets.UI;
+using OpenTK;
namespace osu.Game.Rulesets.Osu.Mods
{
@@ -28,10 +33,23 @@ namespace osu.Game.Rulesets.Osu.Mods
public override double ScoreMultiplier => 1.06;
}
- public class OsuModHardRock : ModHardRock
+ public class OsuModHardRock : ModHardRock, IApplicableMod
{
public override double ScoreMultiplier => 1.06;
public override bool Ranked => true;
+
+ public void ApplyToRulesetContainer(RulesetContainer rulesetContainer)
+ {
+ rulesetContainer.Objects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Y));
+ rulesetContainer.Objects.OfType().ForEach(s =>
+ {
+ var newControlPoints = new List();
+ s.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, OsuPlayfield.BASE_SIZE.Y - c.Y)));
+
+ s.ControlPoints = newControlPoints;
+ s.Curve?.Calculate(); // Recalculate the slider curve
+ });
+ }
}
public class OsuModSuddenDeath : ModSuddenDeath
@@ -78,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;
@@ -88,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;
@@ -108,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/Objects/Drawables/Connections/FollowPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
index 30e6172802..dbf5c6c541 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
@@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
new Box
{
Size = new Vector2(width),
- BlendingMode = BlendingMode.Additive,
+ Blending = BlendingMode.Additive,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Alpha = 0.5f,
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index f68a7a765b..3184b83202 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using OpenTK;
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -38,9 +39,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Colour = AccentColour,
Hit = () =>
{
- if (Judgement.Result != HitResult.None) return false;
+ if (AllJudged)
+ return false;
- Judgement.PositionOffset = Vector2.Zero; //todo: set to correct value
UpdateJudgement(true);
return true;
},
@@ -65,24 +66,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Size = circle.DrawSize;
}
- protected override void CheckJudgement(bool userTriggered)
+ protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
- if (Judgement.TimeOffset > HitObject.HitWindowFor(OsuScoreResult.Hit50))
- Judgement.Result = HitResult.Miss;
+ if (timeOffset > HitObject.HitWindowFor(HitResult.Meh))
+ AddJudgement(new OsuJudgement { Result = HitResult.Miss });
return;
}
- double hitOffset = Math.Abs(Judgement.TimeOffset);
-
- if (hitOffset < HitObject.HitWindowFor(OsuScoreResult.Hit50))
+ AddJudgement(new OsuJudgement
{
- Judgement.Result = HitResult.Hit;
- Judgement.Score = HitObject.ScoreResultForOffset(hitOffset);
- }
- else
- Judgement.Result = HitResult.Miss;
+ Result = HitObject.ScoreResultForOffset(Math.Abs(timeOffset)),
+ PositionOffset = Vector2.Zero //todo: set to correct value
+ });
}
protected override void UpdateInitialState()
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
index ca1b44e1c7..9205f43a6d 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
@@ -3,12 +3,12 @@
using System.ComponentModel;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Judgements;
using osu.Framework.Graphics;
+using System.Linq;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
- public class DrawableOsuHitObject : DrawableHitObject
+ public class DrawableOsuHitObject : DrawableHitObject
{
public const float TIME_PREEMPT = 600;
public const float TIME_FADEIN = 400;
@@ -21,8 +21,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Alpha = 0;
}
- protected override OsuJudgement CreateJudgement() => new OsuJudgement { MaxScore = OsuScoreResult.Hit300 };
-
protected sealed override void UpdateState(ArmedState state)
{
FinishTransforms();
@@ -33,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
UpdatePreemptState();
- using (BeginDelayedSequence(TIME_PREEMPT + Judgement.TimeOffset, true))
+ using (BeginDelayedSequence(TIME_PREEMPT + (Judgements.FirstOrDefault()?.TimeOffset ?? 0), true))
UpdateCurrentState(state);
}
}
@@ -51,6 +49,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected virtual void UpdateCurrentState(ArmedState state)
{
}
+
+ private OsuInputManager osuActionInputManager;
+ internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager);
}
public enum ComboResult
@@ -62,18 +63,4 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
[Description(@"Amazing")]
Perfect
}
-
- public enum OsuScoreResult
- {
- [Description(@"Miss")]
- Miss,
- [Description(@"50")]
- Hit50,
- [Description(@"100")]
- Hit100,
- [Description(@"300")]
- Hit300,
- [Description(@"10")]
- SliderTick
- }
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
index 892d106b02..7755a54e88 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
@@ -9,9 +9,10 @@ using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
- public class DrawableOsuJudgement : DrawableJudgement
+ public class DrawableOsuJudgement : DrawableJudgement
{
- public DrawableOsuJudgement(OsuJudgement judgement) : base(judgement)
+ public DrawableOsuJudgement(OsuJudgement judgement)
+ : base(judgement)
{
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index d5583b0d9d..c5155d1e10 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -8,6 +8,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -64,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Scale = s.Scale,
ComboColour = s.ComboColour,
Samples = s.Samples,
- }),
+ })
};
components.Add(body);
@@ -114,33 +115,31 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
bouncer2.Position = slider.Curve.PositionAt(body.SnakedEnd ?? 0);
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
- if (initialCircle.Judgement?.Result != HitResult.Hit)
+ if (!initialCircle.Judgements.Any(j => j.IsHit))
initialCircle.Position = slider.Curve.PositionAt(progress);
foreach (var c in components) c.UpdateProgress(progress, repeat);
foreach (var t in ticks.Children) t.Tracking = ball.Tracking;
}
- protected override void CheckJudgement(bool userTriggered)
+ protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered && Time.Current >= slider.EndTime)
{
var ticksCount = ticks.Children.Count + 1;
- var ticksHit = ticks.Children.Count(t => t.Judgement.Result == HitResult.Hit);
- if (initialCircle.Judgement.Result == HitResult.Hit)
+ var ticksHit = ticks.Children.Count(t => t.Judgements.Any(j => j.IsHit));
+ if (initialCircle.Judgements.Any(j => j.IsHit))
ticksHit++;
var hitFraction = (double)ticksHit / ticksCount;
- if (hitFraction == 1 && initialCircle.Judgement.Score == OsuScoreResult.Hit300)
- Judgement.Score = OsuScoreResult.Hit300;
- else if (hitFraction >= 0.5 && initialCircle.Judgement.Score >= OsuScoreResult.Hit100)
- Judgement.Score = OsuScoreResult.Hit100;
+ if (hitFraction == 1 && initialCircle.Judgements.Any(j => j.Result == HitResult.Great))
+ AddJudgement(new OsuJudgement { Result = HitResult.Great });
+ else if (hitFraction >= 0.5 && initialCircle.Judgements.Any(j => j.Result >= HitResult.Good))
+ AddJudgement(new OsuJudgement { Result = HitResult.Good });
else if (hitFraction > 0)
- Judgement.Score = OsuScoreResult.Hit50;
+ AddJudgement(new OsuJudgement { Result = HitResult.Meh });
else
- Judgement.Score = OsuScoreResult.Miss;
-
- Judgement.Result = Judgement.Score != OsuScoreResult.Miss ? HitResult.Hit : HitResult.Miss;
+ AddJudgement(new OsuJudgement { Result = HitResult.Miss });
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
index 2a50b23047..53b3427fc4 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
@@ -4,10 +4,10 @@
using System;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Judgements;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -22,8 +22,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public override bool RemoveWhenNotAlive => false;
- protected override OsuJudgement CreateJudgement() => new OsuJudgement { MaxScore = OsuScoreResult.SliderTick };
-
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
{
this.sliderTick = sliderTick;
@@ -49,13 +47,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
};
}
- protected override void CheckJudgement(bool userTriggered)
+ protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
- if (Judgement.TimeOffset >= 0)
- {
- Judgement.Result = Tracking ? HitResult.Hit : HitResult.Miss;
- Judgement.Score = Tracking ? OsuScoreResult.SliderTick : OsuScoreResult.Miss;
- }
+ if (timeOffset >= 0)
+ AddJudgement(new OsuJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss });
}
protected override void UpdatePreemptState()
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index 4a0b8422f1..98dd40b0e6 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.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.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
@@ -10,6 +11,7 @@ using OpenTK.Graphics;
using osu.Game.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Allocation;
+using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Screens.Ranking;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
@@ -106,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1);
- protected override void CheckJudgement(bool userTriggered)
+ protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (Time.Current < HitObject.StartTime) return;
@@ -128,26 +130,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (!userTriggered && Time.Current >= spinner.EndTime)
{
if (Progress >= 1)
- {
- Judgement.Score = OsuScoreResult.Hit300;
- Judgement.Result = HitResult.Hit;
- }
+ AddJudgement(new OsuJudgement { Result = HitResult.Great });
else if (Progress > .9)
- {
- Judgement.Score = OsuScoreResult.Hit100;
- Judgement.Result = HitResult.Hit;
- }
+ AddJudgement(new OsuJudgement { Result = HitResult.Good });
else if (Progress > .75)
- {
- Judgement.Score = OsuScoreResult.Hit50;
- Judgement.Result = HitResult.Hit;
- }
- else
- {
- Judgement.Score = OsuScoreResult.Miss;
- if (Time.Current >= spinner.EndTime)
- Judgement.Result = HitResult.Miss;
- }
+ AddJudgement(new OsuJudgement { Result = HitResult.Meh });
+ else if (Time.Current >= spinner.EndTime)
+ AddJudgement(new OsuJudgement { Result = HitResult.Miss });
}
}
@@ -165,6 +154,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
glow.Colour = colours.BlueDark;
}
+ protected override void Update()
+ {
+ disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton);
+
+ base.Update();
+ }
+
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
index 0c3c4f0a6d..a7384b8f8e 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
@@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
new TrianglesPiece
{
RelativeSizeAxes = Axes.Both,
- BlendingMode = BlendingMode.Additive,
+ Blending = BlendingMode.Additive,
Alpha = 0.5f,
}
};
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs
index e5cf10b88a..7912108901 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs
@@ -16,14 +16,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
- BlendingMode = BlendingMode.Additive;
+ Blending = BlendingMode.Additive;
Alpha = 0;
Children = new Drawable[]
{
new TrianglesPiece
{
- BlendingMode = BlendingMode.Additive,
+ Blending = BlendingMode.Additive,
RelativeSizeAxes = Axes.Both,
Alpha = 0.2f,
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs
index 97d7de35cf..a80d163246 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
- BlendingMode = BlendingMode.Additive;
+ Blending = BlendingMode.Additive;
Alpha = 0;
Children = new Drawable[]
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs
index 8a7b353da1..0adfed2710 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- BlendingMode = BlendingMode.Additive,
+ Blending = BlendingMode.Additive,
Alpha = 0.5f
}
};
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
index dbfd9b291b..1986b1431b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
@@ -1,10 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
+using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
@@ -37,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
this.slider = slider;
Masking = true;
AutoSizeAxes = Axes.Both;
- BlendingMode = BlendingMode.Additive;
+ Blending = BlendingMode.Additive;
Origin = Anchor.Centre;
BorderThickness = 10;
BorderColour = Color4.Orange;
@@ -96,11 +98,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return base.OnMouseMove(state);
}
+ // If the current time is between the start and end of the slider, we should track mouse input regardless of the cursor position.
+ public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => canCurrentlyTrack || base.ReceiveMouseInputAt(screenSpacePos);
+
private bool tracking;
public bool Tracking
{
get { return tracking; }
- set
+ private set
{
if (value == tracking) return;
@@ -117,8 +122,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
base.Update();
+ // Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position.
if (Time.Current < slider.EndTime)
- Tracking = canCurrentlyTrack && lastState != null && ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) && lastState.Mouse.HasMainButtonPressed;
+ Tracking = canCurrentlyTrack && lastState != null && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
}
public void UpdateProgress(double progress, int repeat)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
index 680f987274..2082e9a27b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
path = new Path
{
- BlendingMode = BlendingMode.None,
+ Blending = BlendingMode.None,
},
}
},
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBouncer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBouncer.cs
index 942f166241..a06aaff7fb 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBouncer.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBouncer.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
this.isEnd = isEnd;
AutoSizeAxes = Axes.Both;
- BlendingMode = BlendingMode.Additive;
+ Blending = BlendingMode.Additive;
Origin = Anchor.Centre;
Children = new Drawable[]
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
index cd808abe9d..6577c7fd50 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
@@ -66,21 +66,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}
}
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
- {
- Tracking |= state.Mouse.HasMainButtonPressed;
- return base.OnMouseDown(state, args);
- }
-
- protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
- {
- Tracking &= state.Mouse.HasMainButtonPressed;
- return base.OnMouseUp(state, args);
- }
-
protected override bool OnMouseMove(InputState state)
{
- Tracking |= state.Mouse.HasMainButtonPressed;
mousePosition = Parent.ToLocalSpace(state.Mouse.NativeState.Position);
return base.OnMouseMove(state);
}
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index 7a311f1467..e6bfd8a277 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -4,10 +4,10 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
using OpenTK;
-using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Objects
{
@@ -42,30 +42,30 @@ namespace osu.Game.Rulesets.Osu.Objects
public virtual bool NewCombo { get; set; }
public int ComboIndex { get; set; }
- public double HitWindowFor(OsuScoreResult result)
+ public double HitWindowFor(HitResult result)
{
switch (result)
{
default:
return 300;
- case OsuScoreResult.Hit50:
+ case HitResult.Meh:
return 150;
- case OsuScoreResult.Hit100:
+ case HitResult.Good:
return 80;
- case OsuScoreResult.Hit300:
+ case HitResult.Great:
return 30;
}
}
- public OsuScoreResult ScoreResultForOffset(double offset)
+ public HitResult ScoreResultForOffset(double offset)
{
- if (offset < HitWindowFor(OsuScoreResult.Hit300))
- return OsuScoreResult.Hit300;
- if (offset < HitWindowFor(OsuScoreResult.Hit100))
- return OsuScoreResult.Hit100;
- if (offset < HitWindowFor(OsuScoreResult.Hit50))
- return OsuScoreResult.Hit50;
- return OsuScoreResult.Miss;
+ if (offset < HitWindowFor(HitResult.Great))
+ return HitResult.Great;
+ if (offset < HitWindowFor(HitResult.Good))
+ return HitResult.Good;
+ if (offset < HitWindowFor(HitResult.Meh))
+ return HitResult.Meh;
+ return HitResult.Miss;
}
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 4d8c030ede..056bde4005 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Objects
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
- double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier / difficultyPoint.SpeedMultiplier;
+ double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;
Velocity = scoringDistance / timingPoint.BeatLength;
TickDistance = scoringDistance / difficulty.SliderTickRate;
diff --git a/osu.Game.Rulesets.Osu/OpenTK.dll.config b/osu.Game.Rulesets.Osu/OpenTK.dll.config
index 627e9f6009..5620e3d9e2 100644
--- a/osu.Game.Rulesets.Osu/OpenTK.dll.config
+++ b/osu.Game.Rulesets.Osu/OpenTK.dll.config
@@ -1,7 +1,3 @@
-
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/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs
index a29b128e07..a65d28cec0 100644
--- a/osu.Game.Rulesets.Osu/OsuInputManager.cs
+++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs
@@ -2,36 +2,15 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
-using System.Linq;
-using osu.Framework.Input;
using osu.Framework.Input.Bindings;
-using osu.Game.Input.Bindings;
-using OpenTK.Input;
-using KeyboardState = osu.Framework.Input.KeyboardState;
-using MouseState = osu.Framework.Input.MouseState;
+using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Osu
{
- public class OsuInputManager : DatabasedKeyBindingInputManager
+ public class OsuInputManager : RulesetInputManager
{
public OsuInputManager(RulesetInfo ruleset) : base(ruleset, 0, SimultaneousBindingMode.Unique)
{
-
- }
- protected override void TransformState(InputState state)
- {
- base.TransformState(state);
-
- var mouse = state.Mouse as MouseState;
- var keyboard = state.Keyboard as KeyboardState;
-
- if (mouse != null && keyboard != null)
- {
- if (mouse.IsPressed(MouseButton.Left))
- keyboard.Keys = keyboard.Keys.Concat(new[] { Key.LastKey + 1 });
- if (mouse.IsPressed(MouseButton.Right))
- keyboard.Keys = keyboard.Keys.Concat(new[] { Key.LastKey + 2 });
- }
}
}
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index 75b7be01a4..7650a91d7a 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using OpenTK.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
@@ -10,12 +9,9 @@ using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.OsuDifficulty;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.UI;
-using osu.Game.Screens.Play;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Osu.Scoring;
-using osu.Game.Rulesets.Scoring;
using osu.Game.Overlays.Settings;
using osu.Framework.Input.Bindings;
@@ -27,10 +23,10 @@ namespace osu.Game.Rulesets.Osu
public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[]
{
- new KeyBinding(Key.Z, OsuAction.LeftButton),
- new KeyBinding(Key.X, OsuAction.RightButton),
- new KeyBinding(Key.LastKey + 1, OsuAction.LeftButton),
- new KeyBinding(Key.LastKey + 2, OsuAction.RightButton),
+ new KeyBinding(InputKey.Z, OsuAction.LeftButton),
+ new KeyBinding(InputKey.X, OsuAction.RightButton),
+ new KeyBinding(InputKey.MouseLeft, OsuAction.LeftButton),
+ new KeyBinding(InputKey.MouseRight, OsuAction.RightButton),
};
public override IEnumerable GetBeatmapStatistics(WorkingBeatmap beatmap) => new[]
@@ -114,24 +110,12 @@ 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);
public override string Description => "osu!";
- public override IEnumerable CreateGameplayKeys() => new KeyCounter[]
- {
- new KeyCounterKeyboard(Key.Z),
- new KeyCounterKeyboard(Key.X),
- new KeyCounterMouse(MouseButton.Left),
- new KeyCounterMouse(MouseButton.Right)
- };
-
- public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor();
-
public override SettingsSubsection CreateSettings() => new OsuSettings();
public override int LegacyID => 0;
diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
index b92f1bc60a..f82c6ce3b2 100644
--- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
+++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
@@ -9,6 +9,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
using System;
using System.Diagnostics;
using osu.Framework.Graphics;
+using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays;
@@ -89,20 +90,20 @@ namespace osu.Game.Rulesets.Osu.Replays
double endTime = (prev as IHasEndTime)?.EndTime ?? prev.StartTime;
// Make the cursor stay at a hitObject as long as possible (mainly for autopilot).
- if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
+ if (h.StartTime - h.HitWindowFor(HitResult.Miss) > endTime + h.HitWindowFor(HitResult.Meh) + 50)
{
- if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
- if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
+ if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(HitResult.Meh), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
+ if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(HitResult.Miss), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
}
- else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
+ else if (h.StartTime - h.HitWindowFor(HitResult.Meh) > endTime + h.HitWindowFor(HitResult.Meh) + 50)
{
- if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
- if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
+ if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(HitResult.Meh), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
+ if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(HitResult.Meh), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
}
- else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50)
+ else if (h.StartTime - h.HitWindowFor(HitResult.Good) > endTime + h.HitWindowFor(HitResult.Good) + 50)
{
- if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
- if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
+ if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(HitResult.Good), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
+ if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(HitResult.Good), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
}
}
diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs
new file mode 100644
index 0000000000..0811a68392
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs
@@ -0,0 +1,35 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using osu.Framework.Input;
+using osu.Game.Rulesets.Replays;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.Replays
+{
+ public class OsuReplayInputHandler : FramedReplayInputHandler
+ {
+ public OsuReplayInputHandler(Replay replay)
+ : base(replay)
+ {
+ }
+
+ public override List GetPendingStates()
+ {
+ List actions = new List();
+
+ if (CurrentFrame?.MouseLeft ?? false) actions.Add(OsuAction.LeftButton);
+ if (CurrentFrame?.MouseRight ?? false) actions.Add(OsuAction.RightButton);
+
+ return new List
+ {
+ new ReplayState
+ {
+ Mouse = new ReplayMouseState(ToScreenSpace(Position ?? Vector2.Zero)),
+ PressedActions = actions
+ }
+ };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
index 856ca0c98d..d299faaae2 100644
--- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
@@ -3,6 +3,8 @@
using System.Collections.Generic;
using osu.Framework.Extensions;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
@@ -12,73 +14,95 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Osu.Scoring
{
- internal class OsuScoreProcessor : ScoreProcessor
+ internal class OsuScoreProcessor : ScoreProcessor
{
public OsuScoreProcessor()
{
}
- public OsuScoreProcessor(RulesetContainer rulesetContainer)
+ public OsuScoreProcessor(RulesetContainer rulesetContainer)
: base(rulesetContainer)
{
}
- protected override void Reset()
- {
- base.Reset();
+ private float hpDrainRate;
- Health.Value = 1;
- Accuracy.Value = 1;
+ private readonly Dictionary scoreResultCounts = new Dictionary();
+ private readonly Dictionary comboResultCounts = new Dictionary();
+
+ protected override void SimulateAutoplay(Beatmap beatmap)
+ {
+ hpDrainRate = beatmap.BeatmapInfo.Difficulty.DrainRate;
+
+ foreach (var obj in beatmap.HitObjects)
+ {
+ var slider = obj as Slider;
+ if (slider != null)
+ {
+ // Head
+ AddJudgement(new OsuJudgement { Result = HitResult.Great });
+
+ // Ticks
+ foreach (var unused in slider.Ticks)
+ AddJudgement(new OsuJudgement { Result = HitResult.Great });
+ }
+
+ AddJudgement(new OsuJudgement { Result = HitResult.Great });
+ }
+ }
+
+ protected override void Reset(bool storeResults)
+ {
+ base.Reset(storeResults);
scoreResultCounts.Clear();
comboResultCounts.Clear();
}
- private readonly Dictionary scoreResultCounts = new Dictionary();
- private readonly Dictionary comboResultCounts = new Dictionary();
-
public override void PopulateScore(Score score)
{
base.PopulateScore(score);
- score.Statistics[@"300"] = scoreResultCounts.GetOrDefault(OsuScoreResult.Hit300);
- score.Statistics[@"100"] = scoreResultCounts.GetOrDefault(OsuScoreResult.Hit100);
- score.Statistics[@"50"] = scoreResultCounts.GetOrDefault(OsuScoreResult.Hit50);
- score.Statistics[@"x"] = scoreResultCounts.GetOrDefault(OsuScoreResult.Miss);
+ score.Statistics[@"300"] = scoreResultCounts.GetOrDefault(HitResult.Great);
+ score.Statistics[@"100"] = scoreResultCounts.GetOrDefault(HitResult.Good);
+ score.Statistics[@"50"] = scoreResultCounts.GetOrDefault(HitResult.Meh);
+ score.Statistics[@"x"] = scoreResultCounts.GetOrDefault(HitResult.Miss);
}
- protected override void OnNewJudgement(OsuJudgement judgement)
+ protected override void OnNewJudgement(Judgement judgement)
{
- if (judgement != null)
- {
- if (judgement.Result != HitResult.None)
- {
- scoreResultCounts[judgement.Score] = scoreResultCounts.GetOrDefault(judgement.Score) + 1;
- comboResultCounts[judgement.Combo] = comboResultCounts.GetOrDefault(judgement.Combo) + 1;
- }
+ base.OnNewJudgement(judgement);
- switch (judgement.Result)
- {
- case HitResult.Hit:
- Health.Value += 0.1f;
- break;
- case HitResult.Miss:
- Health.Value -= 0.2f;
- break;
- }
+ var osuJudgement = (OsuJudgement)judgement;
+
+ if (judgement.Result != HitResult.None)
+ {
+ scoreResultCounts[judgement.Result] = scoreResultCounts.GetOrDefault(judgement.Result) + 1;
+ comboResultCounts[osuJudgement.Combo] = comboResultCounts.GetOrDefault(osuJudgement.Combo) + 1;
}
- int score = 0;
- int maxScore = 0;
-
- foreach (var j in Judgements)
+ switch (judgement.Result)
{
- score += j.ScoreValue;
- maxScore += j.MaxScoreValue;
- }
+ case HitResult.Great:
+ Health.Value += (10.2 - hpDrainRate) * 0.02;
+ break;
- TotalScore.Value = score;
- Accuracy.Value = (double)score / maxScore;
+ case HitResult.Good:
+ Health.Value += (8 - hpDrainRate) * 0.02;
+ break;
+
+ case HitResult.Meh:
+ Health.Value += (4 - hpDrainRate) * 0.02;
+ break;
+
+ /*case HitResult.SliderTick:
+ Health.Value += Math.Max(7 - hpDrainRate, 0) * 0.01;
+ break;*/
+
+ case HitResult.Miss:
+ Health.Value -= hpDrainRate * 0.04;
+ break;
+ }
}
}
}
diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
index 9b88c9d1b3..1bb4e8493b 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
@@ -10,12 +10,13 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Connections;
using osu.Game.Rulesets.UI;
using System.Linq;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.UI.Cursor;
namespace osu.Game.Rulesets.Osu.UI
{
- public class OsuPlayfield : Playfield
+ public class OsuPlayfield : Playfield
{
private readonly Container approachCircles;
private readonly Container judgementLayer;
@@ -67,11 +68,11 @@ namespace osu.Game.Rulesets.Osu.UI
AddInternal(new GameplayCursor());
}
- public override void Add(DrawableHitObject h)
+ public override void Add(DrawableHitObject h)
{
h.Depth = (float)h.HitObject.StartTime;
- IDrawableHitObjectWithProxiedApproach c = h as IDrawableHitObjectWithProxiedApproach;
+ var c = h as IDrawableHitObjectWithProxiedApproach;
if (c != null)
approachCircles.Add(c.ProxiedLayer.CreateProxy());
@@ -85,12 +86,15 @@ namespace osu.Game.Rulesets.Osu.UI
.OrderBy(h => h.StartTime).OfType();
}
- public override void OnJudgement(DrawableHitObject judgedObject)
+ public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
- DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgedObject.Judgement)
+ var osuJudgement = (OsuJudgement)judgement;
+ var osuObject = (OsuHitObject)judgedObject.HitObject;
+
+ DrawableOsuJudgement explosion = new DrawableOsuJudgement(osuJudgement)
{
Origin = Anchor.Centre,
- Position = judgedObject.HitObject.StackedEndPosition + judgedObject.Judgement.PositionOffset
+ Position = osuObject.StackedEndPosition + osuJudgement.PositionOffset
};
judgementLayer.Add(explosion);
diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
index 917322de44..668a440c4a 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
@@ -7,16 +7,17 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Beatmaps;
-using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
+using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Osu.UI
{
- public class OsuRulesetContainer : RulesetContainer
+ public class OsuRulesetContainer : RulesetContainer
{
public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset)
@@ -29,11 +30,11 @@ namespace osu.Game.Rulesets.Osu.UI
protected override BeatmapProcessor CreateBeatmapProcessor() => new OsuBeatmapProcessor();
- protected override Playfield CreatePlayfield() => new OsuPlayfield();
+ protected override Playfield CreatePlayfield() => new OsuPlayfield();
- public override PassThroughInputManager CreateKeyBindingInputManager() => new OsuInputManager(Ruleset?.RulesetInfo);
+ public override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo);
- protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h)
+ protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h)
{
var circle = h as HitCircle;
if (circle != null)
@@ -49,6 +50,8 @@ namespace osu.Game.Rulesets.Osu.UI
return null;
}
+ protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay);
+
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f);
}
}
diff --git a/osu.Game.Rulesets.Osu/app.config b/osu.Game.Rulesets.Osu/app.config
new file mode 100644
index 0000000000..faeaf001de
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
index 1422ded407..0963b0ad1b 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
@@ -35,7 +35,7 @@
- $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
True
@@ -78,6 +78,7 @@
+
@@ -109,6 +110,7 @@
osu.licenseheader
+
diff --git a/osu.Game.Rulesets.Osu/packages.config b/osu.Game.Rulesets.Osu/packages.config
index fa6edb9c8f..0b1838ceee 100644
--- a/osu.Game.Rulesets.Osu/packages.config
+++ b/osu.Game.Rulesets.Osu/packages.config
@@ -1,9 +1,4 @@
-
-
-
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
index 44f61e2074..2bf058fc2b 100644
--- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
@@ -39,15 +39,22 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
///
private const float taiko_base_distance = 100;
+ private readonly bool isForCurrentRuleset;
+
protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(HitObject) };
- protected override Beatmap ConvertBeatmap(Beatmap original, bool isForCurrentRuleset)
+ public TaikoBeatmapConverter(bool isForCurrentRuleset)
+ {
+ this.isForCurrentRuleset = isForCurrentRuleset;
+ }
+
+ protected override Beatmap ConvertBeatmap(Beatmap original)
{
// Rewrite the beatmap info to add the slider velocity multiplier
BeatmapInfo info = original.BeatmapInfo.DeepClone();
info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier;
- Beatmap converted = base.ConvertBeatmap(original, isForCurrentRuleset);
+ Beatmap converted = base.ConvertBeatmap(original);
// Post processing step to transform hit objects with the same start time into strong hits
converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x =>
@@ -81,7 +88,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime);
double speedAdjustment = difficultyPoint.SpeedMultiplier;
- double speedAdjustedBeatLength = timingPoint.BeatLength * speedAdjustment;
+ double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment;
// The true distance, accounting for any repeats. This ends up being the drum roll distance later
double distance = distanceData.Distance * repeats * legacy_velocity_multiplier;
@@ -94,7 +101,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
// For some reason, old osu! always uses speedAdjustment to determine the taiko velocity, but
// only uses it to determine osu! velocity if beatmap version < 8. Let's account for that here.
if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
- speedAdjustedBeatLength /= speedAdjustment;
+ speedAdjustedBeatLength *= speedAdjustment;
// The velocity of the osu! hit object - calculated as the velocity of a slider
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
@@ -104,7 +111,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
// If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.Difficulty.SliderTickRate, taikoDuration / repeats);
- if (tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
+ if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
{
List allSamples = curveData != null ? curveData.RepeatSamples : new List(new[] { samples });
diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs
index 78a5b29d36..c9daef8c99 100644
--- a/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs
@@ -1,36 +1,23 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Objects.Drawables;
+
namespace osu.Game.Rulesets.Taiko.Judgements
{
public class TaikoDrumRollTickJudgement : TaikoJudgement
{
- ///
- /// Drum roll ticks don't display judgement text.
- ///
- public override string ResultString => string.Empty;
-
- ///
- /// Drum roll ticks don't display judgement text.
- ///
- public override string MaxResultString => string.Empty;
-
public override bool AffectsCombo => false;
- protected override int NumericResultForScore(TaikoHitResult result)
+ protected override int NumericResultFor(HitResult result)
{
switch (result)
{
default:
return 0;
- case TaikoHitResult.Great:
+ case HitResult.Great:
return 200;
}
}
-
- protected override int NumericResultForAccuracy(TaikoHitResult result)
- {
- return 0;
- }
}
}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoHitResult.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoHitResult.cs
deleted file mode 100644
index 5fd850d6b0..0000000000
--- a/osu.Game.Rulesets.Taiko/Judgements/TaikoHitResult.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System.ComponentModel;
-
-namespace osu.Game.Rulesets.Taiko.Judgements
-{
- public enum TaikoHitResult
- {
- [Description("GOOD")]
- Good,
- [Description("GREAT")]
- Great
- }
-}
diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs
index 7bca59bf11..3cd134f3f7 100644
--- a/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs
@@ -2,86 +2,28 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Judgements;
-using osu.Framework.Extensions;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Taiko.Judgements
{
public class TaikoJudgement : Judgement
{
- ///
- /// The maximum result.
- ///
- public const TaikoHitResult MAX_HIT_RESULT = TaikoHitResult.Great;
-
- ///
- /// The result.
- ///
- public TaikoHitResult TaikoResult;
-
- ///
- /// The result value for the combo portion of the score.
- ///
- public int ResultValueForScore => Result == HitResult.Miss ? 0 : NumericResultForScore(TaikoResult);
-
- ///
- /// The result value for the accuracy portion of the score.
- ///
- public int ResultValueForAccuracy => Result == HitResult.Miss ? 0 : NumericResultForAccuracy(TaikoResult);
-
- ///
- /// The maximum result value for the combo portion of the score.
- ///
- public int MaxResultValueForScore => NumericResultForScore(MAX_HIT_RESULT);
-
- ///
- /// The maximum result value for the accuracy portion of the score.
- ///
- public int MaxResultValueForAccuracy => NumericResultForAccuracy(MAX_HIT_RESULT);
-
- public override string ResultString => TaikoResult.GetDescription();
-
- public override string MaxResultString => MAX_HIT_RESULT.GetDescription();
-
- ///
- /// Whether this Judgement has a secondary hit in the case of strong hits.
- ///
- public virtual bool SecondHit { get; set; }
+ public override HitResult MaxResult => HitResult.Great;
///
/// Computes the numeric result value for the combo portion of the score.
- /// For the accuracy portion of the score (including accuracy percentage), see .
///
/// The result to compute the value for.
/// The numeric result value.
- protected virtual int NumericResultForScore(TaikoHitResult result)
+ protected override int NumericResultFor(HitResult result)
{
switch (result)
{
default:
return 0;
- case TaikoHitResult.Good:
+ case HitResult.Good:
return 100;
- case TaikoHitResult.Great:
- return 300;
- }
- }
-
- ///
- /// Computes the numeric result value for the accuracy portion of the score.
- /// For the combo portion of the score, see .
- ///
- /// The result to compute the value for.
- /// The numeric result value.
- protected virtual int NumericResultForAccuracy(TaikoHitResult result)
- {
- switch (result)
- {
- default:
- return 0;
- case TaikoHitResult.Good:
- return 150;
- case TaikoHitResult.Great:
+ case HitResult.Great:
return 300;
}
}
diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs
index 4996cac39e..f0b57e5c09 100644
--- a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs
@@ -1,25 +1,19 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Taiko.Judgements
{
- public class TaikoStrongHitJudgement : TaikoJudgement, IPartialJudgement
+ public class TaikoStrongHitJudgement : TaikoJudgement
{
- public bool Changed { get; set; }
+ public override bool AffectsCombo => false;
- public override bool SecondHit
+ public TaikoStrongHitJudgement()
{
- get { return base.SecondHit; }
- set
- {
- if (base.SecondHit == value)
- return;
- base.SecondHit = value;
-
- Changed = true;
- }
+ base.Result = HitResult.Perfect;
}
+
+ public new HitResult Result => base.Result;
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
index 7507ee600e..b30b3a1aca 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
@@ -2,16 +2,16 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using OpenTK;
+using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
///
/// A line that scrolls alongside hit objects in the playfield and visualises control points.
///
- public class DrawableBarLine : Container
+ public class DrawableBarLine : DrawableScrollingHitObject
{
///
/// The width of the line tracker.
@@ -34,15 +34,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected readonly BarLine BarLine;
public DrawableBarLine(BarLine barLine)
+ : base(barLine)
{
BarLine = barLine;
Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre;
- RelativePositionAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
-
Width = tracker_width;
Children = new[]
@@ -56,24 +55,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Alpha = 0.75f
}
};
-
- LifetimeStart = BarLine.StartTime - BarLine.ScrollTime * 2;
- LifetimeEnd = BarLine.StartTime + BarLine.ScrollTime;
}
- protected override void LoadComplete()
+ protected override void UpdateState(ArmedState state)
{
- base.LoadComplete();
- this.Delay(BarLine.StartTime - Time.Current).FadeOut(base_fadeout_time * BarLine.ScrollTime / 1000);
- }
-
- private void updateScrollPosition(double time) => this.MoveToX((float)((BarLine.StartTime - time) / BarLine.ScrollTime));
-
- protected override void Update()
- {
- base.Update();
-
- updateScrollPosition(Time.Current);
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs
index c07be915d5..7a4cf1f1f7 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLineMajor.cs
@@ -20,10 +20,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
///
private const float triangle_size = 20f;
+ private readonly Container triangleContainer;
+
public DrawableBarLineMajor(BarLine barLine)
: base(barLine)
{
- Add(new Container
+ Add(triangleContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -53,5 +55,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Tracker.Alpha = 1f;
}
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ using (triangleContainer.BeginAbsoluteSequence(HitObject.StartTime))
+ triangleContainer.FadeOut(150);
+ }
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs
index 8bb78669ca..26aa20f1af 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs
@@ -4,13 +4,12 @@
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
-using OpenTK.Input;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableCentreHit : DrawableHit
{
- protected override Key[] HitKeys { get; } = { Key.F, Key.J };
+ protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre };
public DrawableCentreHit(Hit hit)
: base(hit)
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs
index 434fb9377f..1dbab408de 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs
@@ -4,13 +4,12 @@
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
-using OpenTK.Input;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableCentreHitStrong : DrawableHitStrong
{
- protected override Key[] HitKeys { get; } = { Key.F, Key.J };
+ protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre };
public DrawableCentreHitStrong(Hit hit)
: base(hit)
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
index 74f0f2326d..2396f3bf91 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
@@ -11,6 +11,8 @@ using OpenTK;
using OpenTK.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
@@ -31,30 +33,29 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public DrawableDrumRoll(DrumRoll drumRoll)
: base(drumRoll)
{
- RelativeSizeAxes = Axes.Y;
- AutoSizeAxes = Axes.X;
+ Width = (float)HitObject.Duration;
+
+ Container tickContainer;
+ MainPiece.Add(tickContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ RelativeChildOffset = new Vector2((float)HitObject.StartTime, 0),
+ RelativeChildSize = new Vector2((float)HitObject.Duration, 1)
+ });
foreach (var tick in drumRoll.Ticks)
{
- var newTick = new DrawableDrumRollTick(tick)
- {
- X = (float)((tick.StartTime - HitObject.StartTime) / HitObject.Duration)
- };
-
+ var newTick = new DrawableDrumRollTick(tick);
newTick.OnJudgement += onTickJudgement;
AddNested(newTick);
- MainPiece.Add(newTick);
+ tickContainer.Add(newTick);
}
}
- protected override TaikoJudgement CreateJudgement() => new TaikoJudgement { SecondHit = HitObject.IsStrong };
+ protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece();
- protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece
- {
- Length = (float)(HitObject.Duration / HitObject.ScrollTime),
- PlayfieldLengthReference = () => Parent.DrawSize.X
- };
+ public override bool OnPressed(TaikoAction action) => false;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
@@ -63,20 +64,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
accentDarkColour = colours.YellowDarker;
}
- protected override void LoadComplete()
+ private void onTickJudgement(DrawableHitObject obj, Judgement judgement)
{
- base.LoadComplete();
-
- // This is naive, however it's based on the reasoning that the hit target
- // is further than mid point of the play field, so the time taken to scroll in should always
- // be greater than the time taken to scroll out to the left of the screen.
- // Thus, using PreEmpt here is enough for the drum roll to completely scroll out.
- LifetimeEnd = HitObject.EndTime + HitObject.ScrollTime;
- }
-
- private void onTickJudgement(DrawableHitObject obj)
- {
- if (obj.Judgement.Result == HitResult.Hit)
+ if (judgement.Result > HitResult.Miss)
rollingHits++;
else
rollingHits--;
@@ -87,23 +77,24 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
MainPiece.FadeAccent(newAccent, 100);
}
- protected override void CheckJudgement(bool userTriggered)
+ protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (userTriggered)
return;
- if (Judgement.TimeOffset < 0)
+ if (timeOffset < 0)
return;
- int countHit = NestedHitObjects.Count(o => o.Judgement.Result == HitResult.Hit);
+ int countHit = NestedHitObjects.Count(o => o.AllJudged);
if (countHit > HitObject.RequiredGoodHits)
{
- Judgement.Result = HitResult.Hit;
- Judgement.TaikoResult = countHit >= HitObject.RequiredGreatHits ? TaikoHitResult.Great : TaikoHitResult.Good;
+ AddJudgement(new TaikoJudgement { Result = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good });
+ if (HitObject.IsStrong)
+ AddJudgement(new TaikoStrongHitJudgement());
}
else
- Judgement.Result = HitResult.Miss;
+ AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
}
protected override void UpdateState(ArmedState state)
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
index 0e1cd05de3..8ac67ba0a6 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
@@ -5,7 +5,6 @@ using System;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
-using OpenTK.Input;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
@@ -15,26 +14,37 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public DrawableDrumRollTick(DrumRollTick tick)
: base(tick)
{
+ // Because ticks aren't added by the ScrollingPlayfield, we need to set the following properties ourselves
+ RelativePositionAxes = Axes.X;
+ X = (float)tick.StartTime;
+
FillMode = FillMode.Fit;
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ // We need to set this here because RelativeSizeAxes won't/can't set our size by default with a different RelativeChildSize
+ Width *= Parent.RelativeChildSize.X;
+ }
+
protected override TaikoPiece CreateMainPiece() => new TickPiece
{
Filled = HitObject.FirstTick
};
- protected override TaikoJudgement CreateJudgement() => new TaikoDrumRollTickJudgement { SecondHit = HitObject.IsStrong };
-
- protected override void CheckJudgement(bool userTriggered)
+ protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered)
return;
- if (Math.Abs(Judgement.TimeOffset) < HitObject.HitWindow)
- {
- Judgement.Result = HitResult.Hit;
- Judgement.TaikoResult = TaikoHitResult.Great;
- }
+ if (!(Math.Abs(timeOffset) < HitObject.HitWindow))
+ return;
+
+ AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great });
+ if (HitObject.IsStrong)
+ AddJudgement(new TaikoStrongHitJudgement());
}
protected override void UpdateState(ArmedState state)
@@ -47,14 +57,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
}
}
- protected override void UpdateScrollPosition(double time)
- {
- // Ticks don't move
- }
-
- protected override bool HandleKeyPress(Key key)
- {
- return Judgement.Result == HitResult.None && UpdateJudgement(true);
- }
+ public override bool OnPressed(TaikoAction action) => UpdateJudgement(true);
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
index 5a17355cdb..55eaa8dbb8 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
@@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
-using OpenTK.Input;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
@@ -16,7 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
///
/// A list of keys which can result in hits for this HitObject.
///
- protected abstract Key[] HitKeys { get; }
+ protected abstract TaikoAction[] HitActions { get; }
///
/// Whether the last key pressed is a valid hit key.
@@ -29,47 +28,49 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
FillMode = FillMode.Fit;
}
- protected override void CheckJudgement(bool userTriggered)
+ protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
- if (Judgement.TimeOffset > HitObject.HitWindowGood)
- Judgement.Result = HitResult.Miss;
+ if (timeOffset > HitObject.HitWindowGood)
+ AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
return;
}
- double hitOffset = Math.Abs(Judgement.TimeOffset);
+ double hitOffset = Math.Abs(timeOffset);
if (hitOffset > HitObject.HitWindowMiss)
return;
if (!validKeyPressed)
- Judgement.Result = HitResult.Miss;
+ AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
else if (hitOffset < HitObject.HitWindowGood)
- {
- Judgement.Result = HitResult.Hit;
- Judgement.TaikoResult = hitOffset < HitObject.HitWindowGreat ? TaikoHitResult.Great : TaikoHitResult.Good;
- }
+ AddJudgement(new TaikoJudgement { Result = hitOffset < HitObject.HitWindowGreat ? HitResult.Great : HitResult.Good });
else
- Judgement.Result = HitResult.Miss;
+ AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
}
- protected override bool HandleKeyPress(Key key)
+ public override bool OnPressed(TaikoAction action)
{
- if (Judgement.Result != HitResult.None)
- return false;
-
- validKeyPressed = HitKeys.Contains(key);
+ validKeyPressed = HitActions.Contains(action);
return UpdateJudgement(true);
}
+ protected override void Update()
+ {
+ base.Update();
+
+ Size = BaseSize * Parent.RelativeChildSize;
+ }
+
protected override void UpdateState(ArmedState state)
{
var circlePiece = MainPiece as CirclePiece;
circlePiece?.FlashBox.FinishTransforms();
- using (BeginDelayedSequence(HitObject.StartTime - Time.Current + Judgement.TimeOffset, true))
+ var offset = !AllJudged ? 0 : Time.Current - HitObject.StartTime;
+ using (BeginDelayedSequence(HitObject.StartTime - Time.Current + offset, true))
{
switch (State)
{
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs
index 57e7eea470..48812093c4 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs
@@ -3,10 +3,7 @@
using System;
using System.Linq;
-using osu.Framework.Input;
-using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
-using OpenTK.Input;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
@@ -20,20 +17,21 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
private double firstHitTime;
private bool firstKeyHeld;
- private Key firstHitKey;
+ private TaikoAction firstHitAction;
protected DrawableHitStrong(Hit hit)
: base(hit)
{
}
- protected override TaikoJudgement CreateJudgement() => new TaikoStrongHitJudgement();
+ private bool processedSecondHit;
+ public override bool AllJudged => processedSecondHit && base.AllJudged;
- protected override void CheckJudgement(bool userTriggered)
+ protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
- if (Judgement.Result == HitResult.None)
+ if (!base.AllJudged)
{
- base.CheckJudgement(userTriggered);
+ base.CheckForJudgements(userTriggered, timeOffset);
return;
}
@@ -43,47 +41,51 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
// If we get here, we're assured that the key pressed is the correct secondary key
if (Math.Abs(firstHitTime - Time.Current) < second_hit_window)
- Judgement.SecondHit = true;
+ {
+ AddJudgement(new TaikoStrongHitJudgement());
+ processedSecondHit = true;
+ }
}
- protected override bool HandleKeyPress(Key key)
+ public override bool OnReleased(TaikoAction action)
+ {
+ if (action == firstHitAction)
+ firstKeyHeld = false;
+ return base.OnReleased(action);
+ }
+
+ public override bool OnPressed(TaikoAction action)
{
// Check if we've handled the first key
- if (Judgement.Result == HitResult.None)
+ if (!base.AllJudged)
{
// First key hasn't been handled yet, attempt to handle it
- bool handled = base.HandleKeyPress(key);
+ bool handled = base.OnPressed(action);
if (handled)
{
firstHitTime = Time.Current;
- firstHitKey = key;
+ firstHitAction = action;
+ firstKeyHeld = true;
}
return handled;
}
// If we've already hit the second key, don't handle this object any further
- if (Judgement.SecondHit)
+ if (processedSecondHit)
return false;
// Don't handle represses of the first key
- if (firstHitKey == key)
+ if (firstHitAction == action)
return false;
- // Don't handle invalid hit key presses
- if (!HitKeys.Contains(key))
+ // Don't handle invalid hit action presses
+ if (!HitActions.Contains(action))
return false;
// Assume the intention was to hit the strong hit with both keys only if the first key is still being held down
return firstKeyHeld && UpdateJudgement(true);
}
-
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
- {
- firstKeyHeld = state.Keyboard.Keys.Contains(firstHitKey);
-
- return base.OnKeyDown(state, args);
- }
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs
index 20e8d36105..9f0cd3e061 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs
@@ -4,13 +4,12 @@
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
-using OpenTK.Input;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableRimHit : DrawableHit
{
- protected override Key[] HitKeys { get; } = { Key.D, Key.K };
+ protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim };
public DrawableRimHit(Hit hit)
: base(hit)
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHitStrong.cs
index 4b1bb62bab..294198f3ee 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHitStrong.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHitStrong.cs
@@ -4,13 +4,12 @@
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
-using OpenTK.Input;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableRimHitStrong : DrawableHitStrong
{
- protected override Key[] HitKeys { get; } = { Key.D, Key.K };
+ protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim };
public DrawableRimHitStrong(Hit hit)
: base(hit)
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
index e861af03cf..738902846b 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
@@ -9,12 +9,11 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using OpenTK;
using OpenTK.Graphics;
-using OpenTK.Input;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
@@ -35,9 +34,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
private readonly CircularContainer targetRing;
private readonly CircularContainer expandingRing;
- private readonly Key[] rimKeys = { Key.D, Key.K };
- private readonly Key[] centreKeys = { Key.F, Key.J };
- private Key[] lastKeySet;
+ private readonly TaikoAction[] rimActions = { TaikoAction.LeftRim, TaikoAction.RightRim };
+ private readonly TaikoAction[] centreActions = { TaikoAction.LeftCentre, TaikoAction.RightCentre };
+ private TaikoAction[] lastAction;
///
/// The amount of times the user has hit this swell.
@@ -65,7 +64,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Origin = Anchor.Centre,
Alpha = 0,
RelativeSizeAxes = Axes.Both,
- BlendingMode = BlendingMode.Additive,
+ Blending = BlendingMode.Additive,
Masking = true,
Children = new[]
{
@@ -84,7 +83,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = target_ring_thick_border,
- BlendingMode = BlendingMode.Additive,
+ Blending = BlendingMode.Additive,
Children = new Drawable[]
{
new Box
@@ -118,7 +117,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
});
MainPiece.Add(symbol = new SwellSymbolPiece());
-
}
[BackgroundDependencyLoader]
@@ -129,7 +127,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
targetRing.BorderColour = colours.YellowDark.Opacity(0.25f);
}
- protected override void CheckJudgement(bool userTriggered)
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ // We need to set this here because RelativeSizeAxes won't/can't set our size by default with a different RelativeChildSize
+ Width *= Parent.RelativeChildSize.X;
+ }
+
+ protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (userTriggered)
{
@@ -147,24 +153,17 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint);
if (userHits == HitObject.RequiredHits)
- {
- Judgement.Result = HitResult.Hit;
- Judgement.TaikoResult = TaikoHitResult.Great;
- }
+ AddJudgement(new TaikoJudgement { Result = HitResult.Great });
}
else
{
- if (Judgement.TimeOffset < 0)
+ if (timeOffset < 0)
return;
//TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP
- if (userHits > HitObject.RequiredHits / 2)
- {
- Judgement.Result = HitResult.Hit;
- Judgement.TaikoResult = TaikoHitResult.Good;
- }
- else
- Judgement.Result = HitResult.Miss;
+ AddJudgement(userHits > HitObject.RequiredHits / 2
+ ? new TaikoJudgement { Result = HitResult.Good }
+ : new TaikoJudgement { Result = HitResult.Miss });
}
}
@@ -174,7 +173,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
const float out_transition_time = 300;
double untilStartTime = HitObject.StartTime - Time.Current;
- double untilJudgement = untilStartTime + Judgement.TimeOffset + HitObject.Duration;
+ double untilJudgement = untilStartTime + (Judgements.FirstOrDefault()?.TimeOffset ?? 0) + HitObject.Duration;
targetRing.Delay(untilStartTime - preempt).ScaleTo(target_ring_scale, preempt * 4, Easing.OutQuint);
this.Delay(untilJudgement).FadeOut(out_transition_time, Easing.Out);
@@ -189,36 +188,36 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Expire();
}
- protected override void UpdateScrollPosition(double time)
+ protected override void Update()
{
- // Make the swell stop at the hit target
- double t = Math.Min(HitObject.StartTime, time);
+ base.Update();
+ Size = BaseSize * Parent.RelativeChildSize;
+
+ // Make the swell stop at the hit target
+ X = (float)Math.Max(Time.Current, HitObject.StartTime);
+
+ double t = Math.Min(HitObject.StartTime, Time.Current);
if (t == HitObject.StartTime && !hasStarted)
{
OnStart?.Invoke();
hasStarted = true;
}
-
- base.UpdateScrollPosition(t);
}
- protected override bool HandleKeyPress(Key key)
+ public override bool OnPressed(TaikoAction action)
{
- if (Judgement.Result != HitResult.None)
- return false;
-
// Don't handle keys before the swell starts
if (Time.Current < HitObject.StartTime)
return false;
// Find the keyset which this key corresponds to
- var keySet = rimKeys.Contains(key) ? rimKeys : centreKeys;
+ var keySet = rimActions.Contains(action) ? rimActions : centreActions;
// Ensure alternating keysets
- if (keySet == lastKeySet)
+ if (keySet == lastAction)
return false;
- lastKeySet = keySet;
+ lastAction = keySet;
UpdateJudgement(true);
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
index 510994ed70..7976cbbbc1 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
@@ -1,28 +1,21 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System.Collections.Generic;
using osu.Framework.Graphics;
-using osu.Framework.Input;
+using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using OpenTK;
-using OpenTK.Input;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
- public abstract class DrawableTaikoHitObject : DrawableHitObject
+ public abstract class DrawableTaikoHitObject : DrawableScrollingHitObject, IKeyBindingHandler
where TaikoHitType : TaikoHitObject
{
- ///
- /// A list of keys which this hit object will accept. These are the standard Taiko keys for now.
- /// These should be moved to bindings later.
- ///
- private readonly List validKeys = new List(new[] { Key.D, Key.F, Key.J, Key.K });
-
public override Vector2 OriginPosition => new Vector2(DrawHeight / 2);
+ protected readonly Vector2 BaseSize;
+
protected readonly TaikoPiece MainPiece;
public new TaikoHitType HitObject;
@@ -36,46 +29,16 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Origin = Anchor.Custom;
RelativeSizeAxes = Axes.Both;
- Size = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE);
-
- RelativePositionAxes = Axes.X;
+ Size = BaseSize = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE);
Add(MainPiece = CreateMainPiece());
MainPiece.KiaiMode = HitObject.Kiai;
-
- LifetimeStart = HitObject.StartTime - HitObject.ScrollTime * 2;
}
- protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
-
protected virtual TaikoPiece CreateMainPiece() => new CirclePiece();
- ///
- /// Sets the scroll position of the DrawableHitObject relative to the offset between
- /// a time value and the HitObject's StartTime.
- ///
- ///
- protected virtual void UpdateScrollPosition(double time) => X = (float)((HitObject.StartTime - time) / HitObject.ScrollTime);
+ public abstract bool OnPressed(TaikoAction action);
- protected override void Update()
- {
- UpdateScrollPosition(Time.Current);
- }
-
- protected virtual bool HandleKeyPress(Key key) => false;
-
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
- {
- // Make sure we don't handle held-down keys
- if (args.Repeat)
- return false;
-
- // Check if we've pressed a valid taiko key
- if (!validKeys.Contains(args.Key))
- return false;
-
- // Handle it!
- return HandleKeyPress(args.Key);
- }
+ public virtual bool OnReleased(TaikoAction action) => false;
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs
index ba717371dd..3264b77bc4 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs
@@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
- BlendingMode = BlendingMode.Additive,
+ Blending = BlendingMode.Additive,
Alpha = 0,
AlwaysPresent = true
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs
index 2f5b4eefd6..4642e318c4 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs
@@ -1,23 +1,12 @@
// 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;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
{
public class ElongatedCirclePiece : CirclePiece
{
- ///
- /// As we are being used to define the absolute size of hits, we need to be given a relative reference of our containing playfield container.
- ///
- public Func PlayfieldLengthReference;
-
- ///
- /// The length of this piece as a multiple of the value returned by
- ///
- public float Length;
-
public ElongatedCirclePiece()
{
RelativeSizeAxes = Axes.Y;
@@ -35,7 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
Right = padding,
};
- Width = (PlayfieldLengthReference?.Invoke() ?? 0) * Length + DrawHeight;
+ Width = Parent.DrawSize.X + DrawHeight;
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
index fd9daa269c..526d23f51a 100644
--- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
@@ -80,7 +80,6 @@ namespace osu.Game.Rulesets.Taiko.Objects
ret.Add(new DrumRollTick
{
FirstTick = first,
- ScrollTime = ScrollTime,
TickSpacing = tickSpacing,
StartTime = t,
IsStrong = IsStrong,
diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
index 7e7dc3662f..1be91a7d94 100644
--- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
@@ -1,8 +1,6 @@
// 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.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Taiko.Objects
@@ -24,38 +22,10 @@ namespace osu.Game.Rulesets.Taiko.Objects
///
public const float DEFAULT_STRONG_SIZE = DEFAULT_SIZE * STRONG_SCALE;
- ///
- /// The time taken from the initial (off-screen) spawn position to the centre of the hit target for a of 1000ms.
- ///
- private const double scroll_time = 6000;
-
- ///
- /// Our adjusted taking into consideration local and other speed multipliers.
- ///
- public double ScrollTime;
-
///
/// Whether this HitObject is a "strong" type.
/// Strong hit objects give more points for hitting the hit object with both keys.
///
public bool IsStrong;
-
- ///
- /// Whether this HitObject is in Kiai time.
- ///
- public bool Kiai { get; protected set; }
-
- public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
- {
- base.ApplyDefaults(controlPointInfo, difficulty);
-
- TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
- DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
- EffectControlPoint effectPoint = controlPointInfo.EffectPointAt(StartTime);
-
- ScrollTime = scroll_time * (timingPoint.BeatLength * difficultyPoint.SpeedMultiplier / 1000) / difficulty.SliderMultiplier;
-
- Kiai |= effectPoint.KiaiMode;
- }
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Taiko/OpenTK.dll.config b/osu.Game.Rulesets.Taiko/OpenTK.dll.config
index 627e9f6009..5620e3d9e2 100644
--- a/osu.Game.Rulesets.Taiko/OpenTK.dll.config
+++ b/osu.Game.Rulesets.Taiko/OpenTK.dll.config
@@ -1,7 +1,3 @@
-
diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs
index f6425dd66f..fce0179721 100644
--- a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs
+++ b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs
@@ -4,7 +4,6 @@
using osu.Game.Rulesets.Replays;
using System.Collections.Generic;
using osu.Framework.Input;
-using OpenTK.Input;
namespace osu.Game.Rulesets.Taiko.Replays
{
@@ -17,21 +16,18 @@ namespace osu.Game.Rulesets.Taiko.Replays
public override List GetPendingStates()
{
- var keys = new List();
+ var actions = new List();
if (CurrentFrame?.MouseRight1 == true)
- keys.Add(Key.F);
+ actions.Add(TaikoAction.LeftCentre);
if (CurrentFrame?.MouseRight2 == true)
- keys.Add(Key.J);
+ actions.Add(TaikoAction.RightCentre);
if (CurrentFrame?.MouseLeft1 == true)
- keys.Add(Key.D);
+ actions.Add(TaikoAction.LeftRim);
if (CurrentFrame?.MouseLeft2 == true)
- keys.Add(Key.K);
+ actions.Add(TaikoAction.RightRim);
- return new List
- {
- new InputState { Keyboard = new ReplayKeyboardState(keys) }
- };
+ return new List { new ReplayState { PressedActions = actions } };
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
index 647a1381c6..abdda9676f 100644
--- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
+++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
@@ -1,47 +1,25 @@
// 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.Beatmaps;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.UI;
-using OpenTK;
namespace osu.Game.Rulesets.Taiko.Scoring
{
- internal class TaikoScoreProcessor : ScoreProcessor
+ internal class TaikoScoreProcessor : ScoreProcessor
{
///
- /// The maximum score achievable.
- /// Does _not_ include bonus score - for bonus score see .
- ///
- private const int max_score = 1000000;
-
- ///
- /// The amount of the score attributed to combo.
- ///
- private const double combo_portion_max = max_score * 0.2;
-
- ///
- /// The amount of the score attributed to accuracy.
- ///
- private const double accuracy_portion_max = max_score * 0.8;
-
- ///
- /// The factor used to determine relevance of combos.
- ///
- private const double combo_base = 4;
-
- ///
- /// The HP awarded by a hit.
+ /// The HP awarded by a hit.
///
private const double hp_hit_great = 0.03;
///
- /// The HP awarded for a hit.
+ /// The HP awarded for a hit.
///
private const double hp_hit_good = 0.011;
@@ -75,50 +53,23 @@ namespace osu.Game.Rulesets.Taiko.Scoring
///
/// Taiko fails at the end of the map if the player has not half-filled their HP bar.
///
- public override bool HasFailed => totalHits == maxTotalHits && Health.Value <= 0.5;
-
- ///
- /// The cumulative combo portion of the score.
- ///
- private double comboScore => combo_portion_max * comboPortion / maxComboPortion;
-
- ///
- /// The cumulative accuracy portion of the score.
- ///
- private double accuracyScore => accuracy_portion_max * Math.Pow(Accuracy, 3.6) * totalHits / maxTotalHits;
-
- ///
- /// The cumulative bonus score.
- /// This is added on top of , thus the total score can exceed .
- ///
- private double bonusScore;
-
- ///
- /// The multiple of the original score added to the combo portion of the score
- /// for correctly hitting a strong hit object with both keys.
- ///
- private double strongHitScale;
+ public override bool HasFailed => Hits == MaxHits && Health.Value <= 0.5;
private double hpIncreaseTick;
private double hpIncreaseGreat;
private double hpIncreaseGood;
private double hpIncreaseMiss;
- private double maxComboPortion;
- private double comboPortion;
- private int maxTotalHits;
- private int totalHits;
-
public TaikoScoreProcessor()
{
}
- public TaikoScoreProcessor(RulesetContainer rulesetContainer)
+ public TaikoScoreProcessor(RulesetContainer rulesetContainer)
: base(rulesetContainer)
{
}
- protected override void ComputeTargets(Beatmap beatmap)
+ protected override void SimulateAutoplay(Beatmap beatmap)
{
double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, 0.5, 0.75, 0.98));
@@ -127,68 +78,42 @@ namespace osu.Game.Rulesets.Taiko.Scoring
hpIncreaseGood = hpMultiplierNormal * hp_hit_good;
hpIncreaseMiss = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max);
- var strongHits = beatmap.HitObjects.FindAll(o => o is Hit && o.IsStrong);
-
- // This is a linear function that awards:
- // 10 times bonus points for hitting a strong hit object with both keys with 30 strong hit objects in the map
- // 3 times bonus points for hitting a strong hit object with both keys with 120 strong hit objects in the map
- strongHitScale = -7d / 90d * MathHelper.Clamp(strongHits.Count, 30, 120) + 111d / 9d;
-
foreach (var obj in beatmap.HitObjects)
{
if (obj is Hit)
{
- AddJudgement(new TaikoJudgement
- {
- Result = HitResult.Hit,
- TaikoResult = TaikoHitResult.Great,
- SecondHit = obj.IsStrong
- });
+ AddJudgement(new TaikoJudgement { Result = HitResult.Great });
+ if (obj.IsStrong)
+ AddJudgement(new TaikoStrongHitJudgement());
}
else if (obj is DrumRoll)
{
for (int i = 0; i < ((DrumRoll)obj).TotalTicks; i++)
{
- AddJudgement(new TaikoDrumRollTickJudgement
- {
- Result = HitResult.Hit,
- TaikoResult = TaikoHitResult.Great,
- SecondHit = obj.IsStrong
- });
+ AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great });
+
+ if (obj.IsStrong)
+ AddJudgement(new TaikoStrongHitJudgement());
}
- AddJudgement(new TaikoJudgement
- {
- Result = HitResult.Hit,
- TaikoResult = TaikoHitResult.Great,
- SecondHit = obj.IsStrong
- });
+ AddJudgement(new TaikoJudgement { Result = HitResult.Great });
+
+ if (obj.IsStrong)
+ AddJudgement(new TaikoStrongHitJudgement());
}
else if (obj is Swell)
{
- AddJudgement(new TaikoJudgement
- {
- Result = HitResult.Hit,
- TaikoResult = TaikoHitResult.Great
- });
+ AddJudgement(new TaikoJudgement { Result = HitResult.Great });
}
}
-
- maxTotalHits = totalHits;
- maxComboPortion = comboPortion;
}
- protected override void OnNewJudgement(TaikoJudgement judgement)
+ protected override void OnNewJudgement(Judgement judgement)
{
+ base.OnNewJudgement(judgement);
+
bool isTick = judgement is TaikoDrumRollTickJudgement;
- // Don't consider ticks as a type of hit that counts towards map completion
- if (!isTick)
- totalHits++;
-
- // Apply score changes
- addHitScore(judgement);
-
// Apply HP changes
switch (judgement.Result)
{
@@ -197,81 +122,23 @@ namespace osu.Game.Rulesets.Taiko.Scoring
if (!isTick)
Health.Value += hpIncreaseMiss;
break;
- case HitResult.Hit:
- switch (judgement.TaikoResult)
- {
- case TaikoHitResult.Good:
- Health.Value += hpIncreaseGood;
- break;
- case TaikoHitResult.Great:
- if (isTick)
- Health.Value += hpIncreaseTick;
- else
- Health.Value += hpIncreaseGreat;
- break;
- }
+ case HitResult.Good:
+ Health.Value += hpIncreaseGood;
+ break;
+ case HitResult.Great:
+ if (isTick)
+ Health.Value += hpIncreaseTick;
+ else
+ Health.Value += hpIncreaseGreat;
break;
}
-
- calculateScore();
}
- protected override void OnJudgementChanged(TaikoJudgement judgement)
+ protected override void Reset(bool storeResults)
{
- // Apply score changes
- addHitScore(judgement);
-
- calculateScore();
- }
-
- private void addHitScore(TaikoJudgement judgement)
- {
- if (judgement.Result != HitResult.Hit)
- return;
-
- double baseValue = judgement.ResultValueForScore;
-
- // Add increased score for hitting a strong hit object with the second key
- if (judgement.SecondHit)
- baseValue *= strongHitScale;
-
- // Add score to portions
- if (judgement is TaikoDrumRollTickJudgement)
- bonusScore += baseValue;
- else
- {
- // A relevance factor that needs to be applied to make higher combos more relevant
- // Value is capped at 400 combo
- double comboRelevance = Math.Min(Math.Log(400, combo_base), Math.Max(0.5, Math.Log(Combo.Value, combo_base)));
-
- comboPortion += baseValue * comboRelevance;
- }
- }
-
- private void calculateScore()
- {
- int scoreForAccuracy = 0;
- int maxScoreForAccuracy = 0;
-
- foreach (var j in Judgements)
- {
- scoreForAccuracy += j.ResultValueForAccuracy;
- maxScoreForAccuracy += j.MaxResultValueForAccuracy;
- }
-
- Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy;
- TotalScore.Value = comboScore + accuracyScore + bonusScore;
- }
-
- protected override void Reset()
- {
- base.Reset();
+ base.Reset(storeResults);
Health.Value = 0;
-
- bonusScore = 0;
- comboPortion = 0;
- totalHits = 0;
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs
index 33e9510f1c..35f849c704 100644
--- a/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs
@@ -135,6 +135,6 @@ namespace osu.Game.Rulesets.Taiko
return difficulty;
}
- protected override BeatmapConverter CreateBeatmapConverter() => new TaikoBeatmapConverter();
+ protected override BeatmapConverter CreateBeatmapConverter() => new TaikoBeatmapConverter(true);
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Taiko/TaikoInputManager.cs b/osu.Game.Rulesets.Taiko/TaikoInputManager.cs
new file mode 100644
index 0000000000..1e3c0fbcf2
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/TaikoInputManager.cs
@@ -0,0 +1,29 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.ComponentModel;
+using osu.Framework.Input.Bindings;
+using osu.Game.Rulesets.UI;
+
+namespace osu.Game.Rulesets.Taiko
+{
+ public class TaikoInputManager : RulesetInputManager
+ {
+ public TaikoInputManager(RulesetInfo ruleset)
+ : base(ruleset, 0, SimultaneousBindingMode.Unique)
+ {
+ }
+ }
+
+ public enum TaikoAction
+ {
+ [Description("Left (Rim)")]
+ LeftRim,
+ [Description("Left (Centre)")]
+ LeftCentre,
+ [Description("Right (Centre)")]
+ RightCentre,
+ [Description("Right (Rim)")]
+ RightRim
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
index 4d4bbb7bc5..ea0f0eae1a 100644
--- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
@@ -1,18 +1,15 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using OpenTK.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Rulesets.UI;
-using osu.Game.Screens.Play;
using System.Collections.Generic;
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.Taiko.Scoring;
+using osu.Framework.Input.Bindings;
namespace osu.Game.Rulesets.Taiko
{
@@ -20,6 +17,18 @@ namespace osu.Game.Rulesets.Taiko
{
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new TaikoRulesetContainer(this, beatmap, isForCurrentRuleset);
+ public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[]
+ {
+ new KeyBinding(InputKey.D, TaikoAction.LeftRim),
+ new KeyBinding(InputKey.F, TaikoAction.LeftCentre),
+ new KeyBinding(InputKey.J, TaikoAction.RightCentre),
+ new KeyBinding(InputKey.K, TaikoAction.RightRim),
+ new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre),
+ new KeyBinding(InputKey.MouseLeft, TaikoAction.RightCentre),
+ new KeyBinding(InputKey.MouseRight, TaikoAction.LeftRim),
+ new KeyBinding(InputKey.MouseRight, TaikoAction.RightRim),
+ };
+
public override IEnumerable GetModsFor(ModType type)
{
switch (type)
@@ -84,24 +93,12 @@ 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 };
- public override IEnumerable CreateGameplayKeys() => new KeyCounter[]
- {
- new KeyCounterKeyboard(Key.D),
- new KeyCounterKeyboard(Key.F),
- new KeyCounterKeyboard(Key.J),
- new KeyCounterKeyboard(Key.K)
- };
-
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new TaikoDifficultyCalculator(beatmap);
- public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor();
-
public override int LegacyID => 1;
public TaikoRuleset(RulesetInfo rulesetInfo)
diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
index 779471b8dc..41b66c286b 100644
--- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Allocation;
using osu.Game.Graphics;
@@ -13,15 +12,19 @@ namespace osu.Game.Rulesets.Taiko.UI
///
/// Text that is shown as judgement when a hit object is hit or missed.
///
- public class DrawableTaikoJudgement : DrawableJudgement
+ public class DrawableTaikoJudgement : DrawableJudgement
{
+ public readonly DrawableHitObject JudgedObject;
+
///
/// Creates a new judgement text.
///
+ /// The object which is being judged.
/// The judgement to visualise.
- public DrawableTaikoJudgement(TaikoJudgement judgement)
+ public DrawableTaikoJudgement(DrawableHitObject judgedObject, Judgement judgement)
: base(judgement)
{
+ JudgedObject = judgedObject;
}
[BackgroundDependencyLoader]
@@ -29,28 +32,19 @@ namespace osu.Game.Rulesets.Taiko.UI
{
switch (Judgement.Result)
{
- case HitResult.Hit:
- switch (Judgement.TaikoResult)
- {
- case TaikoHitResult.Good:
- Colour = colours.GreenLight;
- break;
- case TaikoHitResult.Great:
- Colour = colours.BlueLight;
- break;
- }
+ case HitResult.Good:
+ Colour = colours.GreenLight;
+ break;
+ case HitResult.Great:
+ Colour = colours.BlueLight;
break;
}
}
protected override void LoadComplete()
{
- switch (Judgement.Result)
- {
- case HitResult.Hit:
- this.MoveToY(-100, 500);
- break;
- }
+ if (Judgement.IsHit)
+ this.MoveToY(-100, 500);
base.LoadComplete();
}
diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs
index cb849a11c7..830a01bfbc 100644
--- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs
+++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs
@@ -8,7 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Taiko.Judgements;
+using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Rulesets.Taiko.UI
@@ -18,17 +18,17 @@ namespace osu.Game.Rulesets.Taiko.UI
///
internal class HitExplosion : CircularContainer
{
- public readonly TaikoJudgement Judgement;
+ public readonly DrawableHitObject JudgedObject;
private readonly Box innerFill;
private readonly bool isRim;
- public HitExplosion(TaikoJudgement judgement, bool isRim)
+ public HitExplosion(DrawableHitObject judgedObject, bool isRim)
{
this.isRim = isRim;
- Judgement = judgement;
+ JudgedObject = judgedObject;
Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre;
diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
index 0255171284..5e79166bec 100644
--- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
+++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
@@ -3,13 +3,12 @@
using System;
using OpenTK;
-using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Input;
+using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Taiko.UI
@@ -36,8 +35,8 @@ namespace osu.Game.Rulesets.Taiko.UI
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.X,
X = -middle_split / 2,
- RimKey = Key.D,
- CentreKey = Key.F
+ RimAction = TaikoAction.LeftRim,
+ CentreAction = TaikoAction.LeftCentre
},
new TaikoHalfDrum(true)
{
@@ -47,8 +46,8 @@ namespace osu.Game.Rulesets.Taiko.UI
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.X,
X = middle_split / 2,
- RimKey = Key.K,
- CentreKey = Key.J
+ RimAction = TaikoAction.RightRim,
+ CentreAction = TaikoAction.RightCentre
}
};
}
@@ -56,17 +55,17 @@ namespace osu.Game.Rulesets.Taiko.UI
///
/// A half-drum. Contains one centre and one rim hit.
///
- private class TaikoHalfDrum : Container
+ private class TaikoHalfDrum : Container, IKeyBindingHandler
{
///
/// The key to be used for the rim of the half-drum.
///
- public Key RimKey;
+ public TaikoAction RimAction;
///
/// The key to be used for the centre of the half-drum.
///
- public Key CentreKey;
+ public TaikoAction CentreAction;
private readonly Sprite rim;
private readonly Sprite rimHit;
@@ -91,7 +90,7 @@ namespace osu.Game.Rulesets.Taiko.UI
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Alpha = 0,
- BlendingMode = BlendingMode.Additive,
+ Blending = BlendingMode.Additive,
},
centre = new Sprite
{
@@ -107,7 +106,7 @@ namespace osu.Game.Rulesets.Taiko.UI
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.7f),
Alpha = 0,
- BlendingMode = BlendingMode.Additive
+ Blending = BlendingMode.Additive
}
};
}
@@ -124,20 +123,17 @@ namespace osu.Game.Rulesets.Taiko.UI
centreHit.Colour = colours.Pink;
}
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
+ public bool OnPressed(TaikoAction action)
{
- if (args.Repeat)
- return false;
-
Drawable target = null;
Drawable back = null;
- if (args.Key == CentreKey)
+ if (action == CentreAction)
{
target = centreHit;
back = centre;
}
- else if (args.Key == RimKey)
+ else if (action == RimAction)
{
target = rimHit;
back = rim;
@@ -166,6 +162,8 @@ namespace osu.Game.Rulesets.Taiko.UI
return false;
}
+
+ public bool OnReleased(TaikoAction action) => false;
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs
index bac956a25b..e3e1d485d8 100644
--- a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs
+++ b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs
@@ -7,22 +7,22 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Taiko.Judgements;
+using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Rulesets.Taiko.UI
{
public class KiaiHitExplosion : CircularContainer
{
- public readonly TaikoJudgement Judgement;
+ public readonly DrawableHitObject JudgedObject;
private readonly bool isRim;
- public KiaiHitExplosion(TaikoJudgement judgement, bool isRim)
+ public KiaiHitExplosion(DrawableHitObject judgedObject, bool isRim)
{
this.isRim = isRim;
- Judgement = judgement;
+ JudgedObject = judgedObject;
Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre;
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
index 119cd64fef..d9a216bbfc 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
@@ -14,11 +14,12 @@ using osu.Game.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Extensions.Color4Extensions;
using System.Linq;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
namespace osu.Game.Rulesets.Taiko.UI
{
- public class TaikoPlayfield : Playfield
+ public class TaikoPlayfield : ScrollingPlayfield
{
///
/// Default height of a when inside a .
@@ -35,16 +36,18 @@ namespace osu.Game.Rulesets.Taiko.UI
///
private const float left_area_size = 240;
- protected override Container Content => hitObjectContainer;
private readonly Container hitExplosionContainer;
private readonly Container kiaiExplosionContainer;
- private readonly Container barLineContainer;
private readonly Container judgementContainer;
- private readonly Container hitObjectContainer;
+ protected override Container Content => content;
+ private readonly Container content;
+
private readonly Container topLevelHitContainer;
+ private readonly Container barlineContainer;
+
private readonly Container overlayBackgroundContainer;
private readonly Container backgroundContainer;
@@ -52,6 +55,7 @@ namespace osu.Game.Rulesets.Taiko.UI
private readonly Box background;
public TaikoPlayfield()
+ : base(Axes.X)
{
AddRangeInternal(new Drawable[]
{
@@ -84,7 +88,7 @@ namespace osu.Game.Rulesets.Taiko.UI
{
new Container
{
- Name = "Masked elements",
+ Name = "Masked elements before hit objects",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
Masking = true,
@@ -94,11 +98,7 @@ namespace osu.Game.Rulesets.Taiko.UI
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
- BlendingMode = BlendingMode.Additive,
- },
- barLineContainer = new Container
- {
- RelativeSizeAxes = Axes.Both,
+ Blending = BlendingMode.Additive,
},
new HitTarget
{
@@ -106,27 +106,35 @@ namespace osu.Game.Rulesets.Taiko.UI
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit
- },
- hitObjectContainer = new Container
- {
- RelativeSizeAxes = Axes.Both,
- },
+ }
}
},
+ barlineContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }
+ },
+ content = new Container
+ {
+ Name = "Hit objects",
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
+ Masking = true
+ },
kiaiExplosionContainer = new Container
{
Name = "Kiai hit explosions",
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
- BlendingMode = BlendingMode.Additive
+ Blending = BlendingMode.Additive
},
judgementContainer = new Container
{
Name = "Judgements",
RelativeSizeAxes = Axes.Y,
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
- BlendingMode = BlendingMode.Additive
+ Blending = BlendingMode.Additive
},
}
},
@@ -181,6 +189,8 @@ namespace osu.Game.Rulesets.Taiko.UI
RelativeSizeAxes = Axes.Both,
}
});
+
+ VisibleTimeRange.Value = 6000;
}
[BackgroundDependencyLoader]
@@ -193,56 +203,55 @@ namespace osu.Game.Rulesets.Taiko.UI
background.Colour = colours.Gray0;
}
- public override void Add(DrawableHitObject h)
+ public override void Add(DrawableHitObject h)
{
h.Depth = (float)h.HitObject.StartTime;
base.Add(h);
+ var barline = h as DrawableBarLine;
+ if (barline != null)
+ barlineContainer.Add(barline.CreateProxy());
+
// Swells should be moved at the very top of the playfield when they reach the hit target
var swell = h as DrawableSwell;
if (swell != null)
swell.OnStart += () => topLevelHitContainer.Add(swell.CreateProxy());
}
- public void AddBarLine(DrawableBarLine barLine)
+ public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
- barLineContainer.Add(barLine);
- }
-
- public override void OnJudgement(DrawableHitObject judgedObject)
- {
- bool wasHit = judgedObject.Judgement.Result == HitResult.Hit;
- bool secondHit = judgedObject.Judgement.SecondHit;
-
- judgementContainer.Add(new DrawableTaikoJudgement(judgedObject.Judgement)
+ if (judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null)
{
- Anchor = wasHit ? Anchor.TopLeft : Anchor.CentreLeft,
- Origin = wasHit ? Anchor.BottomCentre : Anchor.Centre,
- RelativePositionAxes = Axes.X,
- X = wasHit ? judgedObject.Position.X : 0,
- });
+ judgementContainer.Add(new DrawableTaikoJudgement(judgedObject, judgement)
+ {
+ Anchor = judgement.IsHit ? Anchor.TopLeft : Anchor.CentreLeft,
+ Origin = judgement.IsHit ? Anchor.BottomCentre : Anchor.Centre,
+ RelativePositionAxes = Axes.X,
+ X = judgement.IsHit ? judgedObject.Position.X : 0,
+ });
+ }
- if (!wasHit)
+ if (!judgement.IsHit)
return;
bool isRim = judgedObject.HitObject is RimHit;
- if (!secondHit)
+ if (judgement is TaikoStrongHitJudgement)
+ hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == judgedObject)?.VisualiseSecondHit();
+ else
{
- if (judgedObject.X >= -0.05f && !(judgedObject is DrawableSwell))
+ if (judgedObject.X >= -0.05f && judgedObject is DrawableHit)
{
// If we're far enough away from the left stage, we should bring outselves in front of it
topLevelHitContainer.Add(judgedObject.CreateProxy());
}
- hitExplosionContainer.Add(new HitExplosion(judgedObject.Judgement, isRim));
+ hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));
if (judgedObject.HitObject.Kiai)
- kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject.Judgement, isRim));
+ kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim));
}
- else
- hitExplosionContainer.Children.FirstOrDefault(e => e.Judgement == judgedObject.Judgement)?.VisualiseSecondHit();
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
index 29f563b735..4e7b676fbc 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
@@ -9,7 +9,6 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Beatmaps;
-using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Scoring;
@@ -18,10 +17,11 @@ using osu.Game.Rulesets.Taiko.Replays;
using OpenTK;
using osu.Game.Rulesets.Beatmaps;
using System.Linq;
+using osu.Framework.Input;
namespace osu.Game.Rulesets.Taiko.UI
{
- public class TaikoRulesetContainer : RulesetContainer
+ public class TaikoRulesetContainer : ScrollingRulesetContainer
{
public TaikoRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset)
@@ -36,11 +36,6 @@ namespace osu.Game.Rulesets.Taiko.UI
private void loadBarLines()
{
- var taikoPlayfield = Playfield as TaikoPlayfield;
-
- if (taikoPlayfield == null)
- return;
-
TaikoHitObject lastObject = Beatmap.HitObjects[Beatmap.HitObjects.Count - 1];
double lastHitTime = 1 + (lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime;
@@ -72,7 +67,7 @@ namespace osu.Game.Rulesets.Taiko.UI
barLine.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.Difficulty);
bool isMajor = currentBeat % (int)currentPoint.TimeSignature == 0;
- taikoPlayfield.AddBarLine(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine));
+ Playfield.Add(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine));
double bl = currentPoint.BeatLength;
if (bl < 800)
@@ -95,15 +90,17 @@ namespace osu.Game.Rulesets.Taiko.UI
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
- protected override BeatmapConverter CreateBeatmapConverter() => new TaikoBeatmapConverter();
+ protected override BeatmapConverter CreateBeatmapConverter() => new TaikoBeatmapConverter(IsForCurrentRuleset);
- protected override Playfield CreatePlayfield() => new TaikoPlayfield
+ public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
+
+ protected override Playfield CreatePlayfield() => new TaikoPlayfield
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft
};
- protected override DrawableHitObject GetVisualRepresentation(TaikoHitObject h)
+ protected override DrawableHitObject GetVisualRepresentation(TaikoHitObject h)
{
var centreHit = h as CentreHit;
if (centreHit != null)
diff --git a/osu.Game.Rulesets.Taiko/app.config b/osu.Game.Rulesets.Taiko/app.config
new file mode 100644
index 0000000000..faeaf001de
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
index 59c6f4db0a..c8915d233e 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
@@ -34,7 +34,7 @@
- $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
True
@@ -52,7 +52,6 @@
-
@@ -86,6 +85,7 @@
+
@@ -100,6 +100,7 @@
osu.licenseheader
+
diff --git a/osu.Game.Rulesets.Taiko/packages.config b/osu.Game.Rulesets.Taiko/packages.config
index 8add43d5d5..0b1838ceee 100644
--- a/osu.Game.Rulesets.Taiko/packages.config
+++ b/osu.Game.Rulesets.Taiko/packages.config
@@ -1,8 +1,4 @@
-
-
+
\ No newline at end of file
diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
index ecaf0db096..eb456d7a63 100644
--- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
@@ -13,7 +13,6 @@ using osu.Framework.Platform;
using osu.Game.IPC;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
-using osu.Game.Rulesets;
namespace osu.Game.Tests.Beatmaps.IO
{
@@ -98,16 +97,14 @@ namespace osu.Game.Tests.Beatmaps.IO
private OsuGameBase loadOsu(GameHost host)
{
+ host.Storage.DeleteDatabase(@"client");
+
var osu = new OsuGameBase();
Task.Run(() => host.Run(osu));
while (!osu.IsLoaded)
Thread.Sleep(1);
- //reset beatmap database (sqlite and storage backing)
- osu.Dependencies.Get().Reset();
- osu.Dependencies.Get().Reset();
-
return osu;
}
diff --git a/osu.Game.Tests/OpenTK.dll.config b/osu.Game.Tests/OpenTK.dll.config
index 627e9f6009..5620e3d9e2 100644
--- a/osu.Game.Tests/OpenTK.dll.config
+++ b/osu.Game.Tests/OpenTK.dll.config
@@ -1,7 +1,3 @@
-
diff --git a/osu.Game.Tests/app.config b/osu.Game.Tests/app.config
new file mode 100644
index 0000000000..faeaf001de
--- /dev/null
+++ b/osu.Game.Tests/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index 8ec68b41be..07190bedb0 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
@@ -30,12 +30,12 @@
false
-
- $(SolutionDir)\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll
+
+ $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll
True
- $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
True
@@ -53,6 +53,7 @@
osu.licenseheader
+
diff --git a/osu.Game.Tests/packages.config b/osu.Game.Tests/packages.config
index 9ad76308d7..af47f642e3 100644
--- a/osu.Game.Tests/packages.config
+++ b/osu.Game.Tests/packages.config
@@ -4,8 +4,8 @@ Copyright (c) 2007-2017 ppy Pty Ltd .
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
-
-
+
+
\ No newline at end of file
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index 82777734bb..458c2304f2 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.IO.Serialization;
+using osu.Game.Storyboards;
namespace osu.Game.Beatmaps
{
@@ -17,7 +18,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,23 +34,30 @@ 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.
///
public double TotalBreakTime => Breaks.Sum(b => b.Duration);
+ ///
+ /// The Beatmap's Storyboard.
+ ///
+ public Storyboard Storyboard = new Storyboard();
+
///
/// 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;
+ Storyboard = original?.Storyboard ?? Storyboard;
}
}
@@ -65,7 +73,6 @@ namespace osu.Game.Beatmaps
public Beatmap(Beatmap original = null)
: base(original)
{
- HitObjects = original?.HitObjects;
}
}
}
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index ec90cabf02..0776669811 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -49,9 +49,18 @@ namespace osu.Game.Beatmaps
public string Path { get; set; }
- [JsonProperty("file_md5")]
+ [JsonProperty("file_sha2")]
public string Hash { get; set; }
+ public bool Hidden { get; set; }
+
+ ///
+ /// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.).
+ ///
+ [Indexed]
+ [JsonProperty("file_md5")]
+ public string MD5Hash { get; set; }
+
// General
public int AudioLeadIn { get; set; }
public bool Countdown { get; set; }
@@ -91,6 +100,11 @@ namespace osu.Game.Beatmaps
public bool Equals(BeatmapInfo other)
{
+ if (ID == 0 || other?.ID == 0)
+ // one of the two BeatmapInfos we are comparing isn't sourced from a database.
+ // fall back to reference equality.
+ return ReferenceEquals(this, other);
+
return ID == other?.ID;
}
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index e388f5b6d1..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
{
@@ -33,11 +36,21 @@ namespace osu.Game.Beatmaps
///
public event Action BeatmapSetAdded;
+ ///
+ /// Fired when a single difficulty has been hidden.
+ ///
+ public event Action BeatmapHidden;
+
///
/// Fired when a is removed from the database.
///
public event Action BeatmapSetRemoved;
+ ///
+ /// Fired when a single difficulty has been restored.
+ ///
+ public event Action BeatmapRestored;
+
///
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
///
@@ -53,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;
@@ -66,16 +83,19 @@ 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);
beatmaps.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s);
+ beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b);
+ beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b);
this.storage = storage;
this.files = files;
this.connection = connection;
this.rulesets = rulesets;
+ this.api = api;
if (importHost != null)
ipc = new BeatmapIPCChannel(importHost, this);
@@ -162,24 +182,102 @@ namespace osu.Game.Beatmaps
// If we have an ID then we already exist in the database.
if (beatmapSetInfo.ID != 0) return;
- lock (beatmaps)
- beatmaps.Add(beatmapSetInfo);
+ 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.
///
- /// The beatmap to delete.
+ /// The beatmap set to delete.
public void Delete(BeatmapSetInfo beatmapSet)
{
- lock (beatmaps)
- if (!beatmaps.Delete(beatmapSet)) return;
+ if (!beatmaps.Delete(beatmapSet)) return;
if (!beatmapSet.Protected)
files.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray());
}
+ ///
+ /// Delete a beatmap difficulty.
+ ///
+ /// The beatmap difficulty to hide.
+ public void Hide(BeatmapInfo beatmap) => beatmaps.Hide(beatmap);
+
+ ///
+ /// Restore a beatmap difficulty.
+ ///
+ /// The beatmap difficulty to restore.
+ public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap);
+
///
/// Returns a to a usable state if it has previously been deleted but not yet purged.
/// Is a no-op for already usable beatmaps.
@@ -187,8 +285,7 @@ namespace osu.Game.Beatmaps
/// The beatmap to restore.
public void Undelete(BeatmapSetInfo beatmapSet)
{
- lock (beatmaps)
- if (!beatmaps.Undelete(beatmapSet)) return;
+ if (!beatmaps.Undelete(beatmapSet)) return;
if (!beatmapSet.Protected)
files.Reference(beatmapSet.Files.Select(f => f.FileInfo).ToArray());
@@ -248,6 +345,13 @@ namespace osu.Game.Beatmaps
}
}
+ ///
+ /// Refresh an existing instance of a from the store.
+ ///
+ /// A stale instance.
+ /// A fresh instance.
+ public BeatmapSetInfo Refresh(BeatmapSetInfo beatmapSet) => QueryBeatmapSet(s => s.ID == beatmapSet.ID);
+
///
/// Perform a lookup query on available s.
///
@@ -255,7 +359,7 @@ namespace osu.Game.Beatmaps
/// Results from the provided query.
public List QueryBeatmapSets(Expression> query)
{
- lock (beatmaps) return beatmaps.QueryAndPopulate(query);
+ return beatmaps.QueryAndPopulate(query);
}
///
@@ -265,15 +369,12 @@ namespace osu.Game.Beatmaps
/// The first result for the provided query, or null if no results were found.
public BeatmapInfo QueryBeatmap(Func query)
{
- lock (beatmaps)
- {
- BeatmapInfo set = beatmaps.Query().FirstOrDefault(query);
+ BeatmapInfo set = beatmaps.Query().FirstOrDefault(query);
- if (set != null)
- beatmaps.Populate(set);
+ if (set != null)
+ beatmaps.Populate(set);
- return set;
- }
+ return set;
}
///
@@ -372,6 +473,7 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapInfo.Path = name;
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
+ beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary
beatmap.BeatmapInfo.Metadata = null;
diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs
index 2ec9a7d759..0f2d8cffa6 100644
--- a/osu.Game/Beatmaps/BeatmapStore.cs
+++ b/osu.Game/Beatmaps/BeatmapStore.cs
@@ -16,11 +16,14 @@ namespace osu.Game.Beatmaps
public event Action BeatmapSetAdded;
public event Action BeatmapSetRemoved;
+ public event Action BeatmapHidden;
+ public event Action BeatmapRestored;
+
///
/// The current version of this store. Used for migrations (see ).
/// The initial version is 1.
///
- protected override int StoreVersion => 2;
+ protected override int StoreVersion => 4;
public BeatmapStore(SQLiteConnection connection)
: base(connection)
@@ -77,6 +80,14 @@ namespace osu.Game.Beatmaps
// cannot migrate; breaking underlying changes.
Reset();
break;
+ case 3:
+ // Added MD5Hash column to BeatmapInfo
+ Connection.MigrateTable();
+ break;
+ case 4:
+ // Added Hidden column to BeatmapInfo
+ Connection.MigrateTable();
+ break;
}
}
}
@@ -96,7 +107,7 @@ namespace osu.Game.Beatmaps
}
///
- /// Delete a to the database.
+ /// Delete a from the database.
///
/// The beatmap to delete.
/// Whether the beatmap's was changed.
@@ -127,6 +138,38 @@ namespace osu.Game.Beatmaps
return true;
}
+ ///
+ /// Hide a in the database.
+ ///
+ /// The beatmap to hide.
+ /// Whether the beatmap's was changed.
+ public bool Hide(BeatmapInfo beatmap)
+ {
+ if (beatmap.Hidden) return false;
+
+ beatmap.Hidden = true;
+ Connection.Update(beatmap);
+
+ BeatmapHidden?.Invoke(beatmap);
+ return true;
+ }
+
+ ///
+ /// Restore a previously hidden .
+ ///
+ /// The beatmap to restore.
+ /// Whether the beatmap's was changed.
+ public bool Restore(BeatmapInfo beatmap)
+ {
+ if (!beatmap.Hidden) return false;
+
+ beatmap.Hidden = false;
+ Connection.Update(beatmap);
+
+ BeatmapRestored?.Invoke(beatmap);
+ return true;
+ }
+
private void cleanupPendingDeletions()
{
Connection.RunInTransaction(() =>
diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs
index 30c7884334..c622d5310c 100644
--- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs
@@ -10,4 +10,4 @@ namespace osu.Game.Beatmaps.ControlPoints
///
public double SpeedMultiplier = 1;
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Beatmaps/DifficultyCalculator.cs b/osu.Game/Beatmaps/DifficultyCalculator.cs
index 474d38aa1b..5071e2c1b6 100644
--- a/osu.Game/Beatmaps/DifficultyCalculator.cs
+++ b/osu.Game/Beatmaps/DifficultyCalculator.cs
@@ -30,11 +30,15 @@ namespace osu.Game.Beatmaps
public abstract class DifficultyCalculator : DifficultyCalculator where T : HitObject
{
+ protected readonly Beatmap Beatmap;
+
protected List Objects;
protected DifficultyCalculator(Beatmap beatmap)
{
- Objects = CreateBeatmapConverter().Convert(beatmap, true).HitObjects;
+ Beatmap = beatmap;
+
+ Objects = CreateBeatmapConverter().Convert(beatmap).HitObjects;
foreach (var h in Objects)
h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty);
diff --git a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs
index ad9a0a787b..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;
///
@@ -23,19 +25,26 @@ namespace osu.Game.Beatmaps.Drawables
///
public Action StartRequested;
- public BeatmapSetHeader Header;
+ public Action DeleteRequested;
- private BeatmapGroupState state;
+ public Action RestoreHiddenRequested;
+
+ public Action HideDifficultyRequested;
+
+ public BeatmapSetHeader Header;
public List BeatmapPanels;
public BeatmapSetInfo BeatmapSet;
+ private BeatmapGroupState state;
public BeatmapGroupState State
{
get { return state; }
set
{
+ state = value;
+
switch (value)
{
case BeatmapGroupState.Expanded:
@@ -54,7 +63,8 @@ namespace osu.Game.Beatmaps.Drawables
panel.State = PanelSelectedState.Hidden;
break;
}
- state = value;
+
+ StateChanged?.Invoke(state);
}
}
@@ -66,14 +76,17 @@ namespace osu.Game.Beatmaps.Drawables
Header = new BeatmapSetHeader(beatmap)
{
GainedSelection = headerGainedSelection,
+ DeleteRequested = b => DeleteRequested(b),
+ RestoreHiddenRequested = b => RestoreHiddenRequested(b),
RelativeSizeAxes = Axes.X,
};
- BeatmapSet.Beatmaps = BeatmapSet.Beatmaps.OrderBy(b => b.StarDifficulty).ToList();
+ BeatmapSet.Beatmaps = BeatmapSet.Beatmaps.Where(b => !b.Hidden).OrderBy(b => b.StarDifficulty).ToList();
BeatmapPanels = BeatmapSet.Beatmaps.Select(b => new BeatmapPanel(b)
{
Alpha = 0,
GainedSelection = panelGainedSelection,
+ HideRequested = p => HideDifficultyRequested?.Invoke(p),
StartRequested = p => { StartRequested?.Invoke(p.Beatmap); },
RelativeSizeAxes = Axes.X,
}).ToList();
@@ -81,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/BeatmapPanel.cs b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs
index 429ecaf416..e216f1b83e 100644
--- a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs
+++ b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs
@@ -5,6 +5,7 @@ using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
@@ -14,16 +15,20 @@ using OpenTK.Graphics;
using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.UserInterface;
namespace osu.Game.Beatmaps.Drawables
{
- public class BeatmapPanel : Panel
+ public class BeatmapPanel : Panel, IHasContextMenu
{
public BeatmapInfo Beatmap;
private readonly Sprite background;
public Action GainedSelection;
public Action StartRequested;
+ public Action EditRequested;
+ public Action HideRequested;
+
private readonly Triangles triangles;
private readonly StarCounter starCounter;
@@ -148,5 +153,12 @@ namespace osu.Game.Beatmaps.Drawables
}
};
}
+
+ public MenuItem[] ContextMenuItems => new MenuItem[]
+ {
+ new OsuMenuItem("Play", MenuItemType.Highlighted, () => StartRequested?.Invoke(this)),
+ new OsuMenuItem("Edit", MenuItemType.Standard, () => EditRequested?.Invoke(this)),
+ new OsuMenuItem("Hide", MenuItemType.Destructive, () => HideRequested?.Invoke(Beatmap)),
+ };
}
}
diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs
index a2457ba78e..ee75b77747 100644
--- a/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs
+++ b/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs
@@ -3,22 +3,31 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.UserInterface;
+using osu.Game.Graphics.UserInterface;
namespace osu.Game.Beatmaps.Drawables
{
- public class BeatmapSetHeader : Panel
+ public class BeatmapSetHeader : Panel, IHasContextMenu
{
public Action GainedSelection;
+
+ public Action DeleteRequested;
+
+ public Action RestoreHiddenRequested;
+
private readonly SpriteText title;
private readonly SpriteText artist;
@@ -148,5 +157,23 @@ namespace osu.Game.Beatmaps.Drawables
foreach (var p in panels)
difficultyIcons.Add(new DifficultyIcon(p.Beatmap));
}
+
+ public MenuItem[] ContextMenuItems
+ {
+ get
+ {
+ List
/// Type of the response (used for deserialisation).
- public class APIRequest : APIRequest
+ public abstract class APIRequest : APIRequest
{
protected override WebRequest CreateWebRequest() => new JsonWebRequest(Uri);
- public APIRequest()
+ protected APIRequest()
{
base.Success += onSuccess;
}
@@ -28,10 +28,36 @@ namespace osu.Game.Online.API
public new event APISuccessHandler Success;
}
+ public abstract class APIDownloadRequest : APIRequest
+ {
+ protected override WebRequest CreateWebRequest()
+ {
+ var request = new WebRequest(Uri);
+ request.DownloadProgress += request_Progress;
+ return request;
+ }
+
+ private void request_Progress(WebRequest request, long current, long total) => API.Scheduler.Add(delegate { Progress?.Invoke(current, total); });
+
+ protected APIDownloadRequest()
+ {
+ base.Success += onSuccess;
+ }
+
+ private void onSuccess()
+ {
+ Success?.Invoke(WebRequest.ResponseData);
+ }
+
+ public event APIProgressHandler Progress;
+
+ public new event APISuccessHandler Success;
+ }
+
///
/// AN API request with no specified response type.
///
- public class APIRequest
+ public abstract class APIRequest
{
///
/// The maximum amount of time before this request will fail.
@@ -42,7 +68,7 @@ namespace osu.Game.Online.API
protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri);
- protected virtual string Uri => $@"{api.Endpoint}/api/v2/{Target}";
+ protected virtual string Uri => $@"{API.Endpoint}/api/v2/{Target}";
private double remainingTime => Math.Max(0, Timeout - (DateTime.Now.TotalMilliseconds() - (startTime ?? 0)));
@@ -52,7 +78,7 @@ namespace osu.Game.Online.API
public double StartTime => startTime ?? -1;
- private APIAccess api;
+ protected APIAccess API;
protected WebRequest WebRequest;
public event APISuccessHandler Success;
@@ -64,7 +90,7 @@ namespace osu.Game.Online.API
public void Perform(APIAccess api)
{
- this.api = api;
+ API = api;
if (checkAndProcessFailure())
return;
@@ -109,9 +135,9 @@ namespace osu.Game.Online.API
/// Whether we are in a failed or cancelled state.
private bool checkAndProcessFailure()
{
- if (api == null || pendingFailure == null) return cancelled;
+ if (API == null || pendingFailure == null) return cancelled;
- api.Scheduler.Add(pendingFailure);
+ API.Scheduler.Add(pendingFailure);
pendingFailure = null;
return true;
}
@@ -119,5 +145,6 @@ namespace osu.Game.Online.API
public delegate void APIFailureHandler(Exception e);
public delegate void APISuccessHandler();
+ public delegate void APIProgressHandler(long current, long total);
public delegate void APISuccessHandler(T content);
}
diff --git a/osu.Game/Online/API/OAuth.cs b/osu.Game/Online/API/OAuth.cs
index c96b21a855..5410bcc55d 100644
--- a/osu.Game/Online/API/OAuth.cs
+++ b/osu.Game/Online/API/OAuth.cs
@@ -27,42 +27,45 @@ namespace osu.Game.Online.API
internal bool AuthenticateWithLogin(string username, string password)
{
- var req = new AccessTokenRequestPassword(username, password)
+ using (var req = new AccessTokenRequestPassword(username, password)
{
Url = $@"{endpoint}/oauth/token",
Method = HttpMethod.POST,
ClientId = clientId,
ClientSecret = clientSecret
- };
-
- try
+ })
{
- req.BlockingPerform();
- }
- catch
- {
- return false;
- }
+ try
+ {
+ req.BlockingPerform();
+ }
+ catch
+ {
+ return false;
+ }
- Token = req.ResponseObject;
- return true;
+ Token = req.ResponseObject;
+ return true;
+ }
}
internal bool AuthenticateWithRefresh(string refresh)
{
try
{
- var req = new AccessTokenRequestRefresh(refresh)
+ using (var req = new AccessTokenRequestRefresh(refresh)
{
Url = $@"{endpoint}/oauth/token",
Method = HttpMethod.POST,
ClientId = clientId,
ClientSecret = clientSecret
- };
- req.BlockingPerform();
+ })
+ {
+ req.BlockingPerform();
- Token = req.ResponseObject;
- return true;
+ Token = req.ResponseObject;
+ return true;
+ }
}
catch
{
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/GetBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs
index ca984d3511..e4763f73ee 100644
--- a/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs
+++ b/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs
@@ -46,6 +46,9 @@ namespace osu.Game.Online.API.Requests
[JsonProperty(@"favourite_count")]
private int favouriteCount { get; set; }
+ [JsonProperty(@"id")]
+ private int onlineId { get; set; }
+
[JsonProperty(@"beatmaps")]
private IEnumerable beatmaps { get; set; }
@@ -53,6 +56,7 @@ namespace osu.Game.Online.API.Requests
{
return new BeatmapSetInfo
{
+ OnlineBeatmapSetID = onlineId,
Metadata = this,
OnlineInfo = new BeatmapSetOnlineInfo
{
diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs
index 966049429e..13bd8d288d 100644
--- a/osu.Game/Online/API/Requests/GetScoresRequest.cs
+++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs
@@ -1,10 +1,13 @@
// 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
@@ -15,15 +18,18 @@ namespace osu.Game.Online.API.Requests
public GetScoresRequest(BeatmapInfo beatmap)
{
+ if (!beatmap.OnlineBeatmapID.HasValue)
+ throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}.");
+
this.beatmap = beatmap;
+
+ Success += onSuccess;
}
- protected override WebRequest CreateWebRequest()
+ private void onSuccess(GetScoresResponse r)
{
- var req = base.CreateWebRequest();
- //req.AddParameter(@"c", beatmap.Hash);
- //req.AddParameter(@"f", beatmap.Path);
- return req;
+ foreach (OnlineScore score in r.Scores)
+ score.ApplyBeatmap(beatmap);
}
protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores";
@@ -32,6 +38,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 01685cc7dc..77683ae857 100644
--- a/osu.Game/Online/Chat/Channel.cs
+++ b/osu.Game/Online/Chat/Channel.cs
@@ -26,9 +26,11 @@ namespace osu.Game.Online.Chat
public readonly SortedList Messages = new SortedList(Comparer.Default);
+ private readonly List pendingMessages = new List();
+
public Bindable Joined = new Bindable();
- public bool ReadOnly => Name != "#lazer";
+ public bool ReadOnly => false;
public const int MAX_HISTORY = 300;
@@ -38,6 +40,16 @@ namespace osu.Game.Online.Chat
}
public event Action> NewMessagesArrived;
+ public event Action PendingMessageResolved;
+ public event Action MessageRemoved;
+
+ public void AddLocalEcho(LocalEchoMessage message)
+ {
+ pendingMessages.Add(message);
+ Messages.Add(message);
+
+ NewMessagesArrived?.Invoke(new[] { message });
+ }
public void AddNewMessages(params Message[] messages)
{
@@ -52,11 +64,42 @@ namespace osu.Game.Online.Chat
private void purgeOldMessages()
{
- int messageCount = Messages.Count;
+ // never purge local echos
+ int messageCount = Messages.Count - pendingMessages.Count;
if (messageCount > MAX_HISTORY)
Messages.RemoveRange(0, messageCount - MAX_HISTORY);
}
+ ///
+ /// Replace or remove a message from the channel.
+ ///
+ /// The local echo message (client-side).
+ /// The response message, or null if the message became invalid.
+ public void ReplaceMessage(LocalEchoMessage echo, Message final)
+ {
+ if (!pendingMessages.Remove(echo))
+ throw new InvalidOperationException("Attempted to remove echo that wasn't present");
+
+ Messages.Remove(echo);
+
+ if (final == null)
+ {
+ MessageRemoved?.Invoke(echo);
+ return;
+ }
+
+ if (Messages.Contains(final))
+ {
+ // message already inserted, so let's throw away this update.
+ // we may want to handle this better in the future, but for the time being api requests are single-threaded so order is assumed.
+ MessageRemoved?.Invoke(echo);
+ return;
+ }
+
+ Messages.Add(final);
+ PendingMessageResolved?.Invoke(echo, final);
+ }
+
public override string ToString() => Name;
}
}
diff --git a/osu.Game/Online/Chat/LocalEchoMessage.cs b/osu.Game/Online/Chat/LocalEchoMessage.cs
new file mode 100644
index 0000000000..079ec58686
--- /dev/null
+++ b/osu.Game/Online/Chat/LocalEchoMessage.cs
@@ -0,0 +1,12 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Online.Chat
+{
+ public class LocalEchoMessage : Message
+ {
+ public LocalEchoMessage() : base(null)
+ {
+ }
+ }
+}
diff --git a/osu.Game/Online/Chat/Message.cs b/osu.Game/Online/Chat/Message.cs
index 4c7e099647..509861868a 100644
--- a/osu.Game/Online/Chat/Message.cs
+++ b/osu.Game/Online/Chat/Message.cs
@@ -11,7 +11,7 @@ namespace osu.Game.Online.Chat
public class Message : IComparable, IEquatable
{
[JsonProperty(@"message_id")]
- public readonly long Id;
+ public readonly long? Id;
//todo: this should be inside sender.
[JsonProperty(@"sender_id")]
@@ -37,14 +37,22 @@ namespace osu.Game.Online.Chat
{
}
- public Message(long id)
+ public Message(long? id)
{
Id = id;
}
- public int CompareTo(Message other) => Id.CompareTo(other.Id);
+ public int CompareTo(Message other)
+ {
+ if (!Id.HasValue)
+ return other.Id.HasValue ? 1 : Timestamp.CompareTo(other.Timestamp);
+ if (!other.Id.HasValue)
+ return -1;
- public bool Equals(Message other) => Id == other?.Id;
+ return Id.Value.CompareTo(other.Id.Value);
+ }
+
+ public virtual bool Equals(Message other) => Id == other?.Id;
public override int GetHashCode() => Id.GetHashCode();
}
diff --git a/osu.Game/OpenTK.dll.config b/osu.Game/OpenTK.dll.config
new file mode 100644
index 0000000000..5620e3d9e2
--- /dev/null
+++ b/osu.Game/OpenTK.dll.config
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index e3525e423c..b4fbdfb252 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -8,7 +8,6 @@ using osu.Game.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
-using osu.Framework.Input;
using osu.Framework.Logging;
using osu.Game.Graphics.UserInterface.Volume;
using osu.Framework.Allocation;
@@ -160,7 +159,7 @@ namespace osu.Game
new VolumeControlReceptor
{
RelativeSizeAxes = Axes.Both,
- ActionRequested = delegate(InputState state) { volume.Adjust(state); }
+ ActionRequested = action => volume.Adjust(action)
},
mainContent = new Container
{
@@ -220,20 +219,37 @@ namespace osu.Game
dependencies.Cache(settings);
dependencies.Cache(social);
+ dependencies.Cache(direct);
dependencies.Cache(chat);
dependencies.Cache(userProfile);
dependencies.Cache(musicController);
dependencies.Cache(notificationOverlay);
dependencies.Cache(dialogOverlay);
- // ensure both overlays aren't presented at the same time
- chat.StateChanged += (container, state) => social.State = state == Visibility.Visible ? Visibility.Hidden : social.State;
- social.StateChanged += (container, state) => chat.State = state == Visibility.Visible ? Visibility.Hidden : chat.State;
+ // ensure only one of these overlays are open at once.
+ var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct };
+ foreach (var overlay in singleDisplayOverlays)
+ {
+ overlay.StateChanged += state =>
+ {
+ if (state == Visibility.Hidden) return;
+
+ foreach (var c in singleDisplayOverlays)
+ {
+ if (c == overlay) continue;
+ c.State = Visibility.Hidden;
+ }
+ };
+ }
LoadComponentAsync(Toolbar = new Toolbar
{
Depth = -4,
- OnHome = delegate { intro?.ChildScreen?.MakeCurrent(); },
+ OnHome = delegate
+ {
+ hideAllOverlays();
+ intro?.ChildScreen?.MakeCurrent();
+ },
}, overlayContent.Add);
settings.StateChanged += delegate
@@ -298,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;
@@ -311,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);
}
@@ -359,6 +378,13 @@ namespace osu.Game
{
base.UpdateAfterChildren();
+ // we only want to apply these restrictions when we are inside a screen stack.
+ // the use case for not applying is in visual/unit tests.
+ bool applyRestrictions = !currentScreen?.AllowBeatmapRulesetChange ?? false;
+
+ Ruleset.Disabled = applyRestrictions;
+ Beatmap.Disabled = applyRestrictions;
+
mainContent.Padding = new MarginPadding { Top = ToolbarOffset };
Cursor.State = currentScreen?.HasLocalCursorDisplayed == false ? Visibility.Visible : Visibility.Hidden;
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index a5600d1ef7..79e03a8141 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -94,20 +94,28 @@ namespace osu.Game
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) =>
dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+ private SQLiteConnection connection;
+
[BackgroundDependencyLoader]
private void load()
{
dependencies.Cache(this);
dependencies.Cache(LocalConfig);
- SQLiteConnection connection = Host.Storage.GetDatabase(@"client");
+ connection = Host.Storage.GetDatabase(@"client");
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(ScoreStore = new ScoreStore(Host.Storage, connection, Host, BeatmapManager));
+ 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());
@@ -142,21 +150,20 @@ 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 baetmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)
- if (lastBeatmap?.Track != b.Track)
+ var trackLoaded = lastBeatmap?.TrackLoaded ?? false;
+
+ // compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)
+ if (!trackLoaded || lastBeatmap?.Track != b.Track)
{
- // this disposal is done to stop the audio track.
- // it may not be exactly what we want for cases beatmaps are reused, as it will
- // trigger a fresh load of contained resources.
- lastBeatmap?.Dispose();
+ if (trackLoaded)
+ {
+ Debug.Assert(lastBeatmap != null);
+ Debug.Assert(lastBeatmap.Track != null);
+
+ lastBeatmap.DisposeTrack();
+ }
Audio.Track.AddItem(b.Track);
}
@@ -193,10 +200,9 @@ namespace osu.Game
globalBinding = new GlobalKeyBindingInputManager(this)
{
RelativeSizeAxes = Axes.Both,
- Child = new OsuTooltipContainer(Cursor)
+ Child = content = new OsuTooltipContainer(Cursor)
{
RelativeSizeAxes = Axes.Both,
- Child = content = new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both },
}
}
}
@@ -237,6 +243,8 @@ namespace osu.Game
LocalConfig.Save();
}
+ connection.Dispose();
+
base.Dispose(isDisposing);
}
}
diff --git a/osu.Game/Overlays/Chat/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelListItem.cs
index f43154ea20..f4cf806044 100644
--- a/osu.Game/Overlays/Chat/ChannelListItem.cs
+++ b/osu.Game/Overlays/Chat/ChannelListItem.cs
@@ -2,6 +2,7 @@
// 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;
@@ -34,7 +35,7 @@ namespace osu.Game.Overlays.Chat
private Color4 topicColour;
private Color4 hoverColour;
- public string[] FilterTerms => new[] { channel.Name };
+ public IEnumerable FilterTerms => new[] { channel.Name };
public bool MatchingFilter
{
set
@@ -76,7 +77,6 @@ namespace osu.Game.Overlays.Chat
Size = new Vector2(text_size),
Shadow = false,
Margin = new MarginPadding { Right = 10f },
- Alpha = 0f,
},
},
},
@@ -109,7 +109,6 @@ namespace osu.Game.Overlays.Chat
TextSize = text_size,
Font = @"Exo2.0-SemiBold",
Shadow = false,
- Alpha = 0.8f,
},
},
},
@@ -151,6 +150,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/ChannelSection.cs b/osu.Game/Overlays/Chat/ChannelSection.cs
index 1f046aff2a..5068b415bc 100644
--- a/osu.Game/Overlays/Chat/ChannelSection.cs
+++ b/osu.Game/Overlays/Chat/ChannelSection.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Chat
public readonly FillFlowContainer ChannelFlow;
public IEnumerable FilterableChildren => ChannelFlow.Children;
- public string[] FilterTerms => new[] { Header };
+ public IEnumerable FilterTerms => new[] { Header };
public bool MatchingFilter
{
set
diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs
index fcebca6fe3..cac0ce01ae 100644
--- a/osu.Game/Overlays/Chat/ChatLine.cs
+++ b/osu.Game/Overlays/Chat/ChatLine.cs
@@ -1,27 +1,27 @@
// 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.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat;
-using OpenTK;
-using OpenTK.Graphics;
-using osu.Framework.Graphics.Effects;
-using osu.Framework.Extensions.Color4Extensions;
-using osu.Framework.Allocation;
using osu.Game.Users;
-using osu.Game.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.UserInterface;
+using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Chat
{
public class ChatLine : Container
{
- public readonly Message Message;
-
- private static readonly Color4[] username_colours = {
+ private static readonly Color4[] username_colours =
+ {
OsuColour.FromHex("588c7e"),
OsuColour.FromHex("b2a367"),
OsuColour.FromHex("c98f65"),
@@ -65,10 +65,10 @@ namespace osu.Game.Overlays.Chat
private const float message_padding = 200;
private const float text_size = 20;
- private Action loadProfile;
-
private Color4 customUsernameColour;
+ private OsuSpriteText timestamp;
+
public ChatLine(Message message)
{
Message = message;
@@ -79,56 +79,80 @@ namespace osu.Game.Overlays.Chat
Padding = new MarginPadding { Left = padding, Right = padding };
}
+ private Message message;
+ private OsuSpriteText username;
+ private OsuTextFlowContainer contentFlow;
+
+ public Message Message
+ {
+ get { return message; }
+ set
+ {
+ if (message == value) return;
+
+ message = value;
+
+ if (!IsLoaded)
+ return;
+
+ updateMessageContent();
+ }
+ }
+
[BackgroundDependencyLoader(true)]
- private void load(OsuColour colours, UserProfileOverlay profile)
+ private void load(OsuColour colours)
{
customUsernameColour = colours.ChatBlue;
- loadProfile = u => profile?.ShowUser(u);
}
+ private bool senderHasBackground => !string.IsNullOrEmpty(message.Sender.Colour);
+
protected override void LoadComplete()
{
base.LoadComplete();
- bool hasBackground = !string.IsNullOrEmpty(Message.Sender.Colour);
- Drawable username = new OsuSpriteText
+ bool hasBackground = senderHasBackground;
+
+ Drawable effectedUsername = username = new OsuSpriteText
{
Font = @"Exo2.0-BoldItalic",
- Text = $@"{Message.Sender.Username}" + (hasBackground ? "" : ":"),
- Colour = hasBackground ? customUsernameColour : username_colours[Message.UserId % username_colours.Length],
+ Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length],
TextSize = text_size,
};
if (hasBackground)
{
// Background effect
- username = username.WithEffect(new EdgeEffect
+ effectedUsername = new Container
{
+ AutoSizeAxes = Axes.Both,
+ Masking = true,
CornerRadius = 4,
- Parameters = new EdgeEffectParameters
- {
- Radius = 1,
- Colour = OsuColour.FromHex(Message.Sender.Colour),
- Type = EdgeEffectType.Shadow,
- }
- }, d =>
- {
- d.Padding = new MarginPadding { Left = 3, Right = 3, Bottom = 1, Top = -3 };
- d.Y = 3;
- })
- // Drop shadow effect
- .WithEffect(new EdgeEffect
- {
- CornerRadius = 4,
- Parameters = new EdgeEffectParameters
+ EdgeEffect = new EdgeEffectParameters
{
Roundness = 1,
Offset = new Vector2(0, 3),
Radius = 3,
Colour = Color4.Black.Opacity(0.3f),
Type = EdgeEffectType.Shadow,
+ },
+ // Drop shadow effect
+ Child = new Container
+ {
+ AutoSizeAxes = Axes.Both,
+ Masking = true,
+ CornerRadius = 4,
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Radius = 1,
+ Colour = OsuColour.FromHex(message.Sender.Colour),
+ Type = EdgeEffectType.Shadow,
+ },
+ Padding = new MarginPadding { Left = 3, Right = 3, Bottom = 1, Top = -3 },
+ Y = 3,
+ Child = username,
}
- });
+ };
}
Children = new Drawable[]
@@ -138,23 +162,20 @@ namespace osu.Game.Overlays.Chat
Size = new Vector2(message_padding, text_size),
Children = new Drawable[]
{
- new OsuSpriteText
+ timestamp = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = @"Exo2.0-SemiBold",
- Text = $@"{Message.Timestamp.LocalDateTime:HH:mm:ss}",
FixedWidth = true,
TextSize = text_size * 0.75f,
- Alpha = 0.4f,
},
- new ClickableContainer
+ new MessageSender(message.Sender)
{
AutoSizeAxes = Axes.Both,
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
- Child = username,
- Action = () => loadProfile(Message.Sender),
+ Child = effectedUsername,
},
}
},
@@ -165,18 +186,48 @@ namespace osu.Game.Overlays.Chat
Padding = new MarginPadding { Left = message_padding + padding },
Children = new Drawable[]
{
- new OsuTextFlowContainer(t =>
+ contentFlow = new OsuTextFlowContainer(t => { t.TextSize = text_size; })
{
- t.TextSize = text_size;
- })
- {
- Text = Message.Content,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
}
}
}
};
+
+ updateMessageContent();
+ FinishTransforms(true);
+ }
+
+ private void updateMessageContent()
+ {
+ this.FadeTo(message is LocalEchoMessage ? 0.4f : 1.0f, 500, Easing.OutQuint);
+ timestamp.FadeTo(message is LocalEchoMessage ? 0 : 1, 500, Easing.OutQuint);
+
+ timestamp.Text = $@"{message.Timestamp.LocalDateTime:HH:mm:ss}";
+ username.Text = $@"{message.Sender.Username}" + (senderHasBackground ? "" : ":");
+ contentFlow.Text = message.Content;
+ }
+
+ private class MessageSender : ClickableContainer, IHasContextMenu
+ {
+ private readonly User sender;
+
+ public MessageSender(User sender)
+ {
+ this.sender = sender;
+ }
+
+ [BackgroundDependencyLoader(true)]
+ private void load(UserProfileOverlay profile)
+ {
+ Action = () => profile?.ShowUser(sender);
+ }
+
+ public MenuItem[] ContextMenuItems => new MenuItem[]
+ {
+ new OsuMenuItem("View Profile", MenuItemType.Highlighted, Action),
+ };
}
}
}
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/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs
index 8a2fa95ed1..7179d8ed90 100644
--- a/osu.Game/Overlays/Chat/DrawableChannel.cs
+++ b/osu.Game/Overlays/Chat/DrawableChannel.cs
@@ -3,11 +3,14 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
+using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Cursor;
using osu.Game.Online.Chat;
namespace osu.Game.Overlays.Chat
@@ -15,7 +18,7 @@ namespace osu.Game.Overlays.Chat
public class DrawableChannel : Container
{
public readonly Channel Channel;
- private readonly FillFlowContainer flow;
+ private readonly ChatLineContainer flow;
private readonly ScrollContainer scroll;
public DrawableChannel(Channel channel)
@@ -32,20 +35,24 @@ namespace osu.Game.Overlays.Chat
// Some chat lines have effects that slightly protrude to the bottom,
// which we do not want to mask away, hence the padding.
Padding = new MarginPadding { Bottom = 5 },
- Children = new Drawable[]
+ Child = new OsuContextMenuContainer
{
- flow = new FillFlowContainer
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Child = flow = new ChatLineContainer
{
- Direction = FillDirection.Vertical,
+ Padding = new MarginPadding { Left = 20, Right = 20 },
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
- Padding = new MarginPadding { Left = 20, Right = 20 }
+ Direction = FillDirection.Vertical,
}
- }
+ },
}
};
- channel.NewMessagesArrived += newMessagesArrived;
+ Channel.NewMessagesArrived += newMessagesArrived;
+ Channel.MessageRemoved += messageRemoved;
+ Channel.PendingMessageResolved += pendingMessageResolved;
}
[BackgroundDependencyLoader]
@@ -63,14 +70,17 @@ namespace osu.Game.Overlays.Chat
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
+
Channel.NewMessagesArrived -= newMessagesArrived;
+ Channel.MessageRemoved -= messageRemoved;
+ Channel.PendingMessageResolved -= pendingMessageResolved;
}
private void newMessagesArrived(IEnumerable newMessages)
{
+ // Add up to last Channel.MAX_HISTORY messages
var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MAX_HISTORY));
- //up to last Channel.MAX_HISTORY messages
flow.AddRange(displayMessages.Select(m => new ChatLine(m)));
if (!IsLoaded) return;
@@ -90,6 +100,35 @@ namespace osu.Game.Overlays.Chat
}
}
+ private void pendingMessageResolved(Message existing, Message updated)
+ {
+ var found = flow.Children.LastOrDefault(c => c.Message == existing);
+ if (found != null)
+ {
+ Trace.Assert(updated.Id.HasValue, "An updated message was returned with no ID.");
+
+ flow.Remove(found);
+ found.Message = updated;
+ flow.Add(found);
+ }
+ }
+
+ private void messageRemoved(Message removed)
+ {
+ flow.Children.FirstOrDefault(c => c.Message == removed)?.FadeColour(Color4.Red, 400).FadeOut(600).Expire();
+ }
+
private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd());
+
+ private class ChatLineContainer : FillFlowContainer
+ {
+ protected override int Compare(Drawable x, Drawable y)
+ {
+ var xC = (ChatLine)x;
+ var yC = (ChatLine)y;
+
+ return xC.Message.CompareTo(yC.Message);
+ }
+ }
}
}
diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs
index e7a4c1c0ab..7c28bdea4d 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -6,23 +6,25 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using OpenTK;
+using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Transforms;
+using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Input;
+using osu.Framework.MathUtils;
using osu.Framework.Threading;
+using osu.Game.Configuration;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Chat;
-using osu.Game.Graphics.UserInterface;
-using osu.Framework.Graphics.UserInterface;
-using OpenTK.Graphics;
-using osu.Framework.Input;
-using osu.Game.Configuration;
-using osu.Game.Graphics;
using osu.Game.Overlays.Chat;
-using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays
{
@@ -37,7 +39,7 @@ namespace osu.Game.Overlays
private readonly LoadingAnimation loading;
- private readonly FocusedTextBox inputTextBox;
+ private readonly FocusedTextBox textbox;
private APIAccess api;
@@ -56,7 +58,7 @@ namespace osu.Game.Overlays
private readonly Box chatBackground;
private readonly Box tabBackground;
- private Bindable chatHeight;
+ public Bindable ChatHeight { get; internal set; }
private readonly Container channelSelectionContainer;
private readonly ChannelSelectionOverlay channelSelection;
@@ -130,7 +132,7 @@ namespace osu.Game.Overlays
},
Children = new Drawable[]
{
- inputTextBox = new FocusedTextBox
+ textbox = new FocusedTextBox
{
RelativeSizeAxes = Axes.Both,
Height = 1,
@@ -160,6 +162,7 @@ namespace osu.Game.Overlays
channelTabs = new ChatTabControl
{
RelativeSizeAxes = Axes.Both,
+ OnRequestLeave = removeChannel,
},
}
},
@@ -169,25 +172,18 @@ 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;
if (state == Visibility.Visible)
{
- inputTextBox.HoldFocus = false;
- if (1f - chatHeight.Value < channel_selection_min_height)
- {
- chatContainer.ResizeHeightTo(1f - channel_selection_min_height, 800, Easing.OutQuint);
- channelSelectionContainer.ResizeHeightTo(channel_selection_min_height, 800, Easing.OutQuint);
- channelSelection.Show();
- chatHeight.Value = 1f - channel_selection_min_height;
- }
+ textbox.HoldFocus = false;
+ if (1f - ChatHeight.Value < channel_selection_min_height)
+ transformChatHeightTo(1f - channel_selection_min_height, 800, Easing.OutQuint);
}
else
- {
- inputTextBox.HoldFocus = true;
- }
+ textbox.HoldFocus = true;
};
}
@@ -201,7 +197,7 @@ namespace osu.Game.Overlays
if (!isDragging)
return base.OnDragStart(state);
- startDragChatHeight = chatHeight.Value;
+ startDragChatHeight = ChatHeight.Value;
return true;
}
@@ -211,7 +207,13 @@ namespace osu.Game.Overlays
{
Trace.Assert(state.Mouse.PositionMouseDown != null);
- chatHeight.Value = startDragChatHeight - (state.Mouse.Position.Y - state.Mouse.PositionMouseDown.Value.Y) / Parent.DrawSize.Y;
+ double targetChatHeight = startDragChatHeight - (state.Mouse.Position.Y - state.Mouse.PositionMouseDown.Value.Y) / Parent.DrawSize.Y;
+
+ // If the channel selection screen is shown, mind its minimum height
+ if (channelSelection.State == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height)
+ targetChatHeight = 1f - channel_selection_min_height;
+
+ ChatHeight.Value = targetChatHeight;
}
return true;
@@ -242,8 +244,8 @@ namespace osu.Game.Overlays
protected override void OnFocus(InputState state)
{
- //this is necessary as inputTextBox is masked away and therefore can't get focus :(
- GetContainingInputManager().ChangeFocus(inputTextBox);
+ //this is necessary as textbox is masked away and therefore can't get focus :(
+ GetContainingInputManager().ChangeFocus(textbox);
base.OnFocus(state);
}
@@ -252,7 +254,7 @@ namespace osu.Game.Overlays
this.MoveToY(0, transition_length, Easing.OutQuint);
this.FadeIn(transition_length, Easing.OutQuint);
- inputTextBox.HoldFocus = true;
+ textbox.HoldFocus = true;
base.PopIn();
}
@@ -261,7 +263,7 @@ namespace osu.Game.Overlays
this.MoveToY(Height, transition_length, Easing.InSine);
this.FadeOut(transition_length, Easing.InSine);
- inputTextBox.HoldFocus = false;
+ textbox.HoldFocus = false;
base.PopOut();
}
@@ -271,14 +273,14 @@ namespace osu.Game.Overlays
this.api = api;
api.Register(this);
- chatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight);
- chatHeight.ValueChanged += h =>
+ ChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight);
+ ChatHeight.ValueChanged += h =>
{
chatContainer.Height = (float)h;
channelSelectionContainer.Height = 1f - (float)h;
tabBackground.FadeTo(h == 1 ? 1 : 0.8f, 200);
};
- chatHeight.TriggerChange();
+ ChatHeight.TriggerChange();
chatBackground.Colour = colours.ChatBlue;
}
@@ -305,6 +307,7 @@ namespace osu.Game.Overlays
addChannel(channels.Find(c => c.Name == @"#lobby"));
channelSelection.OnRequestJoin = addChannel;
+ channelSelection.OnRequestLeave = removeChannel;
channelSelection.Sections = new[]
{
new ChannelSection
@@ -332,11 +335,19 @@ 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;
- inputTextBox.Current.Disabled = currentChannel.ReadOnly;
+ textbox.Current.Disabled = currentChannel.ReadOnly;
channelTabs.Current.Value = value;
var loaded = loadedChannels.Find(d => d.Channel == value);
@@ -391,6 +402,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);
@@ -414,6 +438,7 @@ namespace osu.Game.Overlays
if (fetchReq != null) return;
fetchReq = new GetMessagesRequest(careChannels, lastMessageId);
+
fetchReq.Success += delegate (List messages)
{
foreach (var group in messages.Where(m => m.TargetType == TargetType.Channel).GroupBy(m => m.TargetId))
@@ -424,6 +449,7 @@ namespace osu.Game.Overlays
Debug.Write("success!");
fetchReq = null;
};
+
fetchReq.Failure += delegate
{
Debug.Write("failure!");
@@ -437,53 +463,65 @@ namespace osu.Game.Overlays
{
var postText = textbox.Text;
+ textbox.Text = string.Empty;
+
if (string.IsNullOrEmpty(postText))
return;
+ var target = currentChannel;
+
+ if (target == null) return;
+
if (!api.IsLoggedIn)
{
- currentChannel?.AddNewMessages(new ErrorMessage("Please login to participate in chat!"));
- textbox.Text = string.Empty;
+ target.AddNewMessages(new ErrorMessage("Please login to participate in chat!"));
return;
}
- if (currentChannel == null) return;
-
if (postText[0] == '/')
{
// TODO: handle commands
- currentChannel.AddNewMessages(new ErrorMessage("Chat commands are not supported yet!"));
- textbox.Text = string.Empty;
+ target.AddNewMessages(new ErrorMessage("Chat commands are not supported yet!"));
return;
}
- var message = new Message
+ var message = new LocalEchoMessage
{
Sender = api.LocalUser.Value,
Timestamp = DateTimeOffset.Now,
- TargetType = TargetType.Channel, //TODO: read this from currentChannel
- TargetId = currentChannel.Id,
+ TargetType = TargetType.Channel, //TODO: read this from channel
+ TargetId = target.Id,
Content = postText
};
- textbox.ReadOnly = true;
var req = new PostMessageRequest(message);
- req.Failure += e =>
- {
- textbox.FlashColour(Color4.Red, 1000);
- textbox.ReadOnly = false;
- };
-
- req.Success += m =>
- {
- currentChannel.AddNewMessages(m);
-
- textbox.ReadOnly = false;
- textbox.Text = string.Empty;
- };
+ target.AddLocalEcho(message);
+ req.Failure += e => target.ReplaceMessage(message, null);
+ req.Success += m => target.ReplaceMessage(message, m);
api.Queue(req);
}
+
+ private void transformChatHeightTo(double newChatHeight, double duration = 0, Easing easing = Easing.None)
+ {
+ this.TransformTo(this.PopulateTransform(new TransformChatHeight(), newChatHeight, duration, easing));
+ }
+
+ private class TransformChatHeight : Transform
+ {
+ private double valueAt(double time)
+ {
+ if (time < StartTime) return StartValue;
+ if (time >= EndTime) return EndValue;
+
+ return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
+ }
+
+ public override string TargetMember => "ChatHeight.Value";
+
+ protected override void Apply(ChatOverlay d, double time) => d.ChatHeight.Value = valueAt(time);
+ protected override void ReadIntoStartValue(ChatOverlay d) => StartValue = d.ChatHeight.Value;
+ }
}
}
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/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs
index 98ab5e88f8..3a9e75bd38 100644
--- a/osu.Game/Overlays/Direct/DirectGridPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs
@@ -12,6 +12,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
+using osu.Framework.Input;
namespace osu.Game.Overlays.Direct
{
@@ -25,23 +26,11 @@ namespace osu.Game.Overlays.Direct
public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap)
{
Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image)
- CornerRadius = 4;
- Masking = true;
-
- EdgeEffect = new EdgeEffectParameters
- {
- Type = EdgeEffectType.Shadow,
- Offset = new Vector2(0f, 1f),
- Radius = 3f,
- Colour = Color4.Black.Opacity(0.25f),
- };
}
protected override void LoadComplete()
{
base.LoadComplete();
-
- this.FadeInFromZero(200, Easing.Out);
bottomPanel.LayoutDuration = 200;
bottomPanel.LayoutEasing = Easing.Out;
bottomPanel.Origin = Anchor.BottomLeft;
@@ -50,14 +39,10 @@ namespace osu.Game.Overlays.Direct
[BackgroundDependencyLoader]
private void load(OsuColour colours, LocalisationEngine localisation)
{
- Children = new[]
+ Content.CornerRadius = 4;
+
+ AddRange(new Drawable[]
{
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
- },
- CreateBackground(),
new Box
{
RelativeSizeAxes = Axes.Both,
@@ -185,7 +170,13 @@ namespace osu.Game.Overlays.Direct
new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0),
},
},
- };
+ });
+ }
+
+ protected override bool OnClick(InputState state)
+ {
+ StartDownload();
+ return true;
}
}
}
diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs
index 5b45fc7725..b3502b0827 100644
--- a/osu.Game/Overlays/Direct/DirectListPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectListPanel.cs
@@ -28,35 +28,15 @@ namespace osu.Game.Overlays.Direct
{
RelativeSizeAxes = Axes.X;
Height = height;
- CornerRadius = 5;
- Masking = true;
- EdgeEffect = new EdgeEffectParameters
- {
- Type = EdgeEffectType.Shadow,
- Offset = new Vector2(0f, 1f),
- Radius = 3f,
- Colour = Color4.Black.Opacity(0.25f),
- };
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- this.FadeInFromZero(200, Easing.Out);
}
[BackgroundDependencyLoader]
private void load(LocalisationEngine localisation)
{
- Children = new[]
+ Content.CornerRadius = 5;
+
+ AddRange(new Drawable[]
{
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
- },
- CreateBackground(),
new Box
{
RelativeSizeAxes = Axes.Both,
@@ -144,10 +124,11 @@ namespace osu.Game.Overlays.Direct
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Size = new Vector2(height - vertical_padding * 2),
+ Action = StartDownload
},
},
},
- };
+ });
}
private class DownloadButton : OsuClickableContainer
diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs
index 2f048b0e3d..6f1f581d0b 100644
--- a/osu.Game/Overlays/Direct/DirectPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectPanel.cs
@@ -2,26 +2,179 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
+using OpenTK.Graphics;
+using osu.Framework.Input;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Online.API;
+using osu.Framework.Logging;
+using osu.Game.Overlays.Notifications;
+using osu.Game.Online.API.Requests;
namespace osu.Game.Overlays.Direct
{
public abstract class DirectPanel : Container
{
- protected readonly BeatmapSetInfo SetInfo;
+ public readonly BeatmapSetInfo SetInfo;
+
+ protected Box BlackBackground;
+
+ private const double hover_transition_time = 400;
+
+ private Container content;
+
+ private APIAccess api;
+ private ProgressBar progressBar;
+ private BeatmapManager beatmaps;
+ private NotificationOverlay notifications;
+
+ protected override Container Content => content;
protected DirectPanel(BeatmapSetInfo setInfo)
{
SetInfo = setInfo;
}
+ private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Shadow,
+ Offset = new Vector2(0f, 1f),
+ Radius = 2f,
+ Colour = Color4.Black.Opacity(0.25f),
+ };
+
+ private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Shadow,
+ Offset = new Vector2(0f, 5f),
+ Radius = 10f,
+ Colour = Color4.Black.Opacity(0.3f),
+ };
+
+
+ [BackgroundDependencyLoader(permitNulls: true)]
+ private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications)
+ {
+ this.api = api;
+ this.beatmaps = beatmaps;
+ this.notifications = notifications;
+
+ AddInternal(content = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ EdgeEffect = edgeEffectNormal,
+ Children = new[]
+ {
+ // temporary blackness until the actual background loads.
+ BlackBackground = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ },
+ CreateBackground(),
+ progressBar = new ProgressBar
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Height = 0,
+ Alpha = 0,
+ BackgroundColour = Color4.Black.Opacity(0.7f),
+ FillColour = colours.Blue,
+ Depth = -1,
+ },
+ }
+ });
+
+ var downloadRequest = beatmaps.GetExistingDownload(SetInfo);
+
+ if (downloadRequest != null)
+ attachDownload(downloadRequest);
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint);
+ content.MoveToY(-4, hover_transition_time, Easing.OutQuint);
+
+ return base.OnHover(state);
+ }
+
+ protected override void OnHoverLost(InputState state)
+ {
+ content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint);
+ content.MoveToY(0, hover_transition_time, Easing.OutQuint);
+
+ base.OnHoverLost(state);
+ }
+
+ protected void StartDownload()
+ {
+ if (!api.LocalUser.Value.IsSupporter)
+ {
+ notifications.Post(new SimpleNotification
+ {
+ Icon = FontAwesome.fa_superpowers,
+ Text = "You gotta be a supporter to download for now 'yo"
+ });
+ 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;
+
+ request.Failure += e =>
+ {
+ progressBar.Current.Value = 0;
+ progressBar.FadeOut(500);
+ Logger.Error(e, "Failed to get beatmap download information");
+ };
+
+ request.DownloadProgressed += progress => progressBar.Current.Value = progress;
+
+ request.Success += data =>
+ {
+ progressBar.Current.Value = 1;
+ progressBar.FadeOut(500);
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ this.FadeInFromZero(200, Easing.Out);
+ }
+
protected List GetDifficultyIcons()
{
var icons = new List();
@@ -38,7 +191,11 @@ namespace osu.Game.Overlays.Direct
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill,
- OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out),
+ OnLoadComplete = d =>
+ {
+ d.FadeInFromZero(400, Easing.Out);
+ BlackBackground.Delay(400).FadeOut();
+ },
})
{
RelativeSizeAxes = Axes.Both,
diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs
index b1c7dab778..9c07e1087f 100644
--- a/osu.Game/Overlays/DirectOverlay.cs
+++ b/osu.Game/Overlays/DirectOverlay.cs
@@ -27,10 +27,11 @@ namespace osu.Game.Overlays
private APIAccess api;
private RulesetStore rulesets;
+ private BeatmapManager beatmaps;
private readonly FillFlowContainer resultCountsContainer;
private readonly OsuSpriteText resultCountsText;
- private readonly FillFlowContainer panels;
+ private FillFlowContainer panels;
protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74");
protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71");
@@ -46,20 +47,26 @@ namespace osu.Game.Overlays
set
{
if (beatmapSets?.Equals(value) ?? false) return;
+
beatmapSets = value;
- if (BeatmapSets == null)
- {
- foreach (var p in panels.Children)
- {
- p.FadeOut(200);
- p.Expire();
- }
+ if (beatmapSets == null) return;
- 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(' '));
}
- recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
+ 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);
}
}
@@ -108,13 +115,6 @@ namespace osu.Game.Overlays
},
}
},
- panels = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Spacing = new Vector2(panel_padding),
- Margin = new MarginPadding { Top = 10 },
- },
};
Filter.Search.Current.ValueChanged += text => { if (text != string.Empty) Header.Tabs.Current.Value = DirectTab.Search; };
@@ -161,11 +161,22 @@ namespace osu.Game.Overlays
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours, APIAccess api, RulesetStore rulesets)
+ private void load(OsuColour colours, APIAccess api, RulesetStore rulesets, BeatmapManager beatmaps)
{
this.api = api;
this.rulesets = rulesets;
+ this.beatmaps = beatmaps;
+
resultCountsContainer.Colour = colours.Yellow;
+
+ beatmaps.BeatmapSetAdded += setAdded;
+ }
+
+ private void setAdded(BeatmapSetInfo set)
+ {
+ // 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()
@@ -185,18 +196,38 @@ namespace osu.Game.Overlays
private void recreatePanels(PanelDisplayStyle displayStyle)
{
+ if (panels != null)
+ {
+ panels.FadeOut(200);
+ panels.Expire();
+ panels = null;
+ }
+
if (BeatmapSets == null) return;
- panels.ChildrenEnumerable = BeatmapSets.Select(b =>
- {
- switch (displayStyle)
- {
- case PanelDisplayStyle.Grid:
- return new DirectGridPanel(b) { Width = 400 };
- default:
- return new DirectListPanel(b);
- }
- });
+ var newPanels = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Spacing = new Vector2(panel_padding),
+ Margin = new MarginPadding { Top = 10 },
+ ChildrenEnumerable = BeatmapSets.Select(b =>
+ {
+ switch (displayStyle)
+ {
+ case PanelDisplayStyle.Grid:
+ return new DirectGridPanel(b) { Width = 400 };
+ default:
+ return new DirectListPanel(b);
+ }
+ })
+ };
+
+ LoadComponentAsync(newPanels, p =>
+ {
+ if (panels != null) ScrollFlow.Remove(panels);
+ ScrollFlow.Add(panels = newPanels);
+ });
}
private GetBeatmapSetsRequest getSetsRequest;
@@ -227,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/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs
index 7dd9919e5d..8ebd4ac545 100644
--- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs
+++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs
@@ -3,21 +3,29 @@
using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
+using osu.Game.Overlays.Settings;
namespace osu.Game.Overlays.KeyBinding
{
- public class GlobalKeyBindingsSection : KeyBindingsSection
+ public class GlobalKeyBindingsSection : SettingsSection
{
- private readonly string name;
+ public override FontAwesome Icon => FontAwesome.fa_osu_hot;
+ public override string Header => "Global";
- public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail;
- public override string Header => name;
-
- public GlobalKeyBindingsSection(KeyBindingInputManager manager, string name)
+ public GlobalKeyBindingsSection(KeyBindingInputManager manager)
{
- this.name = name;
+ Add(new DefaultBindingsSubsection(manager));
+ }
- Defaults = manager.DefaultKeyBindings;
+ private class DefaultBindingsSubsection : KeyBindingsSubsection
+ {
+ protected override string Header => string.Empty;
+
+ public DefaultBindingsSubsection(KeyBindingInputManager manager)
+ : base(null)
+ {
+ Defaults = manager.DefaultKeyBindings;
+ }
}
}
}
\ No newline at end of file
diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
index 44aee0e666..4141a502a0 100644
--- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
+++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
@@ -1,7 +1,6 @@
// 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.Allocation;
@@ -11,6 +10,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
+using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Input;
@@ -21,7 +21,7 @@ namespace osu.Game.Overlays.KeyBinding
{
internal class KeyBindingRow : Container, IFilterable
{
- private readonly Enum action;
+ private readonly object action;
private readonly IEnumerable bindings;
private const float transition_time = 150;
@@ -47,9 +47,9 @@ namespace osu.Game.Overlays.KeyBinding
private FillFlowContainer buttons;
- public string[] FilterTerms => new[] { text.Text }.Concat(bindings.Select(b => b.KeyCombination.ReadableString())).ToArray();
+ public IEnumerable FilterTerms => new[] { text.Text }.Concat(bindings.Select(b => b.KeyCombination.ReadableString()));
- public KeyBindingRow(Enum action, IEnumerable bindings)
+ public KeyBindingRow(object action, IEnumerable bindings)
{
this.action = action;
this.bindings = bindings;
@@ -109,24 +109,141 @@ namespace osu.Game.Overlays.KeyBinding
buttons.Add(new KeyButton(b));
}
+ public void RestoreDefaults()
+ {
+ int i = 0;
+ foreach (var d in Defaults)
+ {
+ var button = buttons[i++];
+ button.UpdateKeyCombination(d);
+ store.Update(button.KeyBinding);
+ }
+ }
+
protected override bool OnHover(InputState state)
{
- this.FadeEdgeEffectTo(1, transition_time, Easing.OutQuint);
+ FadeEdgeEffectTo(1, transition_time, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
- this.FadeEdgeEffectTo(0, transition_time, Easing.OutQuint);
+ FadeEdgeEffectTo(0, transition_time, Easing.OutQuint);
base.OnHoverLost(state);
}
- public override bool AcceptsFocus => true;
+ public override bool AcceptsFocus => bindTarget == null;
private KeyButton bindTarget;
+ public bool AllowMainMouseButtons;
+
+ public IEnumerable Defaults;
+
+ private bool isModifier(Key k) => k < Key.F1;
+
+ protected override bool OnClick(InputState state) => true;
+
+ protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ {
+ if (!HasFocus || !bindTarget.IsHovered)
+ return base.OnMouseDown(state, args);
+
+ if (!AllowMainMouseButtons)
+ {
+ switch (args.Button)
+ {
+ case MouseButton.Left:
+ case MouseButton.Right:
+ return true;
+ }
+ }
+
+ bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
+ return true;
+ }
+
+ protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
+ {
+ // don't do anything until the last button is released.
+ if (!HasFocus || state.Mouse.Buttons.Any())
+ return base.OnMouseUp(state, args);
+
+ if (bindTarget.IsHovered)
+ finalise();
+ else
+ updateBindTarget();
+ return true;
+ }
+
+ protected override bool OnWheel(InputState state)
+ {
+ if (HasFocus)
+ {
+ if (bindTarget.IsHovered)
+ {
+ bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
+ finalise();
+ return true;
+ }
+ }
+
+ return base.OnWheel(state);
+ }
+
+ protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
+ {
+ if (!HasFocus)
+ return false;
+
+ switch (args.Key)
+ {
+ case Key.Escape:
+ finalise();
+ return true;
+ case Key.Delete:
+ bindTarget.UpdateKeyCombination(InputKey.None);
+ finalise();
+ return true;
+ }
+
+ bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
+ if (!isModifier(args.Key)) finalise();
+
+ return true;
+ }
+
+ protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
+ {
+ if (!HasFocus) return base.OnKeyUp(state, args);
+
+ finalise();
+ return true;
+ }
+
+ private void finalise()
+ {
+ if (bindTarget != null)
+ {
+ store.Update(bindTarget.KeyBinding);
+
+ bindTarget.IsBinding = false;
+ Schedule(() =>
+ {
+ // schedule to ensure we don't instantly get focus back on next OnMouseClick (see AcceptFocus impl.)
+ bindTarget = null;
+ });
+ }
+
+ if (HasFocus)
+ GetContainingInputManager().ChangeFocus(null);
+
+ pressAKey.FadeOut(300, Easing.OutQuint);
+ pressAKey.Padding = new MarginPadding { Bottom = -pressAKey.DrawHeight };
+ }
+
protected override void OnFocus(InputState state)
{
AutoSizeDuration = 500;
@@ -135,70 +252,21 @@ namespace osu.Game.Overlays.KeyBinding
pressAKey.FadeIn(300, Easing.OutQuint);
pressAKey.Padding = new MarginPadding();
+ updateBindTarget();
base.OnFocus(state);
}
- private bool isModifier(Key k) => k < Key.F1;
-
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
- {
- switch (args.Key)
- {
- case Key.Escape:
- GetContainingInputManager().ChangeFocus(null);
- return true;
- case Key.Delete:
- bindTarget.UpdateKeyCombination(Key.Unknown);
- store.Update(bindTarget.KeyBinding);
- GetContainingInputManager().ChangeFocus(null);
- return true;
- }
-
- if (HasFocus)
- {
- bindTarget.UpdateKeyCombination(state.Keyboard.Keys.ToArray());
- if (!isModifier(args.Key))
- finalise();
- return true;
- }
-
- return base.OnKeyDown(state, args);
- }
-
- protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
- {
- if (HasFocus)
- {
- finalise();
- return true;
- }
-
- return base.OnKeyUp(state, args);
- }
-
- private void finalise()
- {
- store.Update(bindTarget.KeyBinding);
- GetContainingInputManager().ChangeFocus(null);
- }
-
protected override void OnFocusLost(InputState state)
{
- bindTarget.IsBinding = false;
- bindTarget = null;
-
- pressAKey.FadeOut(300, Easing.OutQuint);
- pressAKey.Padding = new MarginPadding { Bottom = -pressAKey.DrawHeight };
+ finalise();
base.OnFocusLost(state);
}
- protected override bool OnClick(InputState state)
+ private void updateBindTarget()
{
if (bindTarget != null) bindTarget.IsBinding = false;
bindTarget = buttons.FirstOrDefault(b => b.IsHovered) ?? buttons.FirstOrDefault();
if (bindTarget != null) bindTarget.IsBinding = true;
-
- return bindTarget != null;
}
private class KeyButton : Container
@@ -296,11 +364,11 @@ namespace osu.Game.Overlays.KeyBinding
}
}
- public void UpdateKeyCombination(params Key[] newCombination)
+ public void UpdateKeyCombination(KeyCombination newCombination)
{
KeyBinding.KeyCombination = newCombination;
Text.Text = KeyBinding.KeyCombination.ReadableString();
}
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSection.cs
deleted file mode 100644
index 19baeca771..0000000000
--- a/osu.Game/Overlays/KeyBinding/KeyBindingsSection.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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.Allocation;
-using osu.Game.Input;
-using osu.Game.Overlays.Settings;
-using osu.Game.Rulesets;
-using OpenTK;
-
-namespace osu.Game.Overlays.KeyBinding
-{
- public abstract class KeyBindingsSection : SettingsSection
- {
- protected IEnumerable Defaults;
-
- protected RulesetInfo Ruleset;
-
- protected KeyBindingsSection()
- {
- FlowContent.Spacing = new Vector2(0, 1);
- }
-
- [BackgroundDependencyLoader]
- private void load(KeyBindingStore store)
- {
- var enumType = Defaults?.FirstOrDefault()?.Action?.GetType();
-
- if (enumType == null) return;
-
- // for now let's just assume a variant of zero.
- // this will need to be implemented in a better way in the future.
- int? variant = null;
- if (Ruleset != null)
- variant = 0;
-
- var bindings = store.Query(Ruleset?.ID, variant);
-
- foreach (Enum v in Enum.GetValues(enumType))
- // one row per valid action.
- Add(new KeyBindingRow(v, bindings.Where(b => b.Action.Equals((int)(object)v))));
- }
- }
-}
\ No newline at end of file
diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs
new file mode 100644
index 0000000000..bd69403831
--- /dev/null
+++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs
@@ -0,0 +1,72 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Framework.Graphics;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Input;
+using osu.Game.Overlays.Settings;
+using osu.Game.Rulesets;
+using OpenTK;
+using osu.Game.Graphics;
+
+namespace osu.Game.Overlays.KeyBinding
+{
+ public abstract class KeyBindingsSubsection : SettingsSubsection
+ {
+ protected IEnumerable Defaults;
+
+ protected RulesetInfo Ruleset;
+
+ private readonly int? variant;
+
+ protected KeyBindingsSubsection(int? variant)
+ {
+ this.variant = variant;
+
+ FlowContent.Spacing = new Vector2(0, 1);
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(KeyBindingStore store)
+ {
+ var bindings = store.Query(Ruleset?.ID, variant);
+
+ foreach (var defaultGroup in Defaults.GroupBy(d => d.Action))
+ {
+ // one row per valid action.
+ Add(new KeyBindingRow(defaultGroup.Key, bindings.Where(b => b.Action.Equals((int)defaultGroup.Key)))
+ {
+ AllowMainMouseButtons = Ruleset != null,
+ Defaults = defaultGroup.Select(d => d.KeyCombination)
+ });
+ }
+
+ Add(new ResetButton
+ {
+ Action = () => Children.OfType().ForEach(k => k.RestoreDefaults())
+ });
+ }
+ }
+
+ internal class ResetButton : OsuButton
+ {
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Text = "Reset";
+ RelativeSizeAxes = Axes.X;
+ Margin = new MarginPadding { Top = 5 };
+ Height = 20;
+
+ Content.CornerRadius = 5;
+
+ BackgroundColour = colours.PinkDark;
+ Triangles.ColourDark = colours.PinkDarker;
+ Triangles.ColourLight = colours.Pink;
+ }
+ }
+}
diff --git a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs
index 20941115e3..885e149cb2 100644
--- a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs
+++ b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs
@@ -2,20 +2,26 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Graphics;
+using osu.Game.Overlays.Settings;
using osu.Game.Rulesets;
namespace osu.Game.Overlays.KeyBinding
{
- public class RulesetBindingsSection : KeyBindingsSection
+ public class RulesetBindingsSection : SettingsSection
{
- public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail;
- public override string Header => Ruleset.Name;
+ public override FontAwesome Icon => FontAwesome.fa_osu_hot;
+ public override string Header => ruleset.Name;
+
+ private readonly RulesetInfo ruleset;
public RulesetBindingsSection(RulesetInfo ruleset)
{
- Ruleset = ruleset;
+ this.ruleset = ruleset;
- Defaults = ruleset.CreateInstance().GetDefaultKeyBindings();
+ var r = ruleset.CreateInstance();
+
+ foreach (var variant in r.AvailableVariants)
+ Add(new VariantBindingsSubsection(ruleset, variant));
}
}
}
\ No newline at end of file
diff --git a/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs
new file mode 100644
index 0000000000..dca5f53b4a
--- /dev/null
+++ b/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.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.Rulesets;
+
+namespace osu.Game.Overlays.KeyBinding
+{
+ public class VariantBindingsSubsection : KeyBindingsSubsection
+ {
+ protected override string Header => variantName;
+ private readonly string variantName;
+
+ public VariantBindingsSubsection(RulesetInfo ruleset, int variant)
+ : base(variant)
+ {
+ Ruleset = ruleset;
+
+ var rulesetInstance = ruleset.CreateInstance();
+
+ variantName = rulesetInstance.GetVariantName(variant);
+ Defaults = rulesetInstance.GetDefaultKeyBindings(variant);
+ }
+ }
+}
\ No newline at end of file
diff --git a/osu.Game/Overlays/KeyBindingOverlay.cs b/osu.Game/Overlays/KeyBindingOverlay.cs
index 7d6ef7ffa6..72c653030c 100644
--- a/osu.Game/Overlays/KeyBindingOverlay.cs
+++ b/osu.Game/Overlays/KeyBindingOverlay.cs
@@ -17,9 +17,9 @@ namespace osu.Game.Overlays
[BackgroundDependencyLoader(permitNulls: true)]
private void load(RulesetStore rulesets, GlobalKeyBindingInputManager global)
{
- AddSection(new GlobalKeyBindingsSection(global, "Global"));
+ AddSection(new GlobalKeyBindingsSection(global));
- foreach (var ruleset in rulesets.Query())
+ foreach (var ruleset in rulesets.AllRulesets)
AddSection(new RulesetBindingsSection(ruleset));
}
diff --git a/osu.Game/Overlays/LoginOverlay.cs b/osu.Game/Overlays/LoginOverlay.cs
index 1bce31c789..58b259fcbb 100644
--- a/osu.Game/Overlays/LoginOverlay.cs
+++ b/osu.Game/Overlays/LoginOverlay.cs
@@ -3,12 +3,12 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Overlays.Settings.Sections.General;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Cursor;
namespace osu.Game.Overlays
{
@@ -34,7 +34,7 @@ namespace osu.Game.Overlays
Colour = Color4.Black,
Alpha = 0.6f,
},
- new Container
+ new OsuContextMenuContainer
{
Width = 360,
AutoSizeAxes = Axes.Y,
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