diff --git a/COMPILING.md b/COMPILING.md
new file mode 100644
index 0000000000..bfcbf6bc2c
--- /dev/null
+++ b/COMPILING.md
@@ -0,0 +1,36 @@
+# Linux
+### 1. Requirements:
+Mono >= 5.4.0 (>= 5.8.0 recommended)
+Please check [here](http://www.mono-project.com/download/) for stable or [here](http://www.mono-project.com/download/alpha/) for an alpha release.
+NuGet >= 4.4.0
+msbuild
+git
+
+### 2. Cloning project
+Clone the entire repository with submodules using
+```
+git clone https://github.com/ppy/osu --recursive
+```
+Then restore NuGet packages from the repository
+```
+nuget restore
+```
+### 3. Compiling
+Simply run `msbuild` where `osu.sln` is located, this will create all binaries in `osu/osu.Desktop/bin/Debug`.
+### 4. Optimizing
+If you want additional performance you can change build type to Release with
+```
+msbuild -p:Configuration=Release
+```
+Additionally, mono provides an AOT utility which attempts to precompile binaries. You can utilize that by running
+```
+mono --aot ./osu\!.exe
+```
+### 5. Troubleshooting
+You may run into trouble with NuGet versioning, as the one in packaging system is almost always out of date. Simply run
+```
+nuget
+sudo nuget update -self
+```
+**Warning** NuGet creates few config files when it's run for the first time.
+Do not run NuGet as root on the first run or you might run into very peculiar issues.
diff --git a/NuGet.config b/NuGet.config
deleted file mode 100644
index 95f993e510..0000000000
--- a/NuGet.config
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/README.md b/README.md
index 856536d22d..47df86f57e 100644
--- a/README.md
+++ b/README.md
@@ -8,8 +8,11 @@ This is still heavily under development and is not intended for end-user use. Th
# Requirements
-- A desktop platform which can compile .NET 4.5 (tested on macOS, linux and windows). We recommend using [Visual Studio Code](https://code.visualstudio.com/) (all platforms) or [Visual Studio Community Edition](https://www.visualstudio.com/) (windows only), both of which are free.
-- Make sure you initialise and keep submodules up-to-date.
+- A desktop platform that can compile .NET 4.6.1. We recommend using [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/) (macOS) or [MonoDevelop](http://www.monodevelop.com/download/) (Linux), all of which are free. [Visual Studio Code](https://code.visualstudio.com/) may also be used but requires further setup steps which are not covered here.
+
+# Getting Started
+- Clone the repository including submodules (`git clone --recurse-submodules https://github.com/ppy/osu`)
+- Build in your IDE of choice (recommended IDEs automatically restore nuget packages; if you are using an alternative make sure to `nuget restore`)
# Contributing
diff --git a/osu-framework b/osu-framework
index 08f85f9bf9..6134dafccb 160000
--- a/osu-framework
+++ b/osu-framework
@@ -1 +1 @@
-Subproject commit 08f85f9bf9a7376aec8dfcde8c7c96d267d8c295
+Subproject commit 6134dafccb3368dac96d837537325c04b89fb8ee
diff --git a/osu-resources b/osu-resources
index 4287ee8043..e01f71160f 160000
--- a/osu-resources
+++ b/osu-resources
@@ -1 +1 @@
-Subproject commit 4287ee8043fb1419017359bc3a5db5dc06bc643f
+Subproject commit e01f71160fb9b3167efcd177c7d7dba9e5d36604
diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs
index 9e13003c3f..c46b0e3d12 100644
--- a/osu.Desktop/Overlays/VersionManager.cs
+++ b/osu.Desktop/Overlays/VersionManager.cs
@@ -110,7 +110,7 @@ namespace osu.Desktop.Overlays
// 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);
+ notificationOverlay.Post(new UpdateCompleteNotification(version));
}
}
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index e4e9807754..3cc4e7f943 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -135,8 +135,8 @@
$(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\NuGet.Squirrel.dll
True
-
- $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
+
+ $(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll
True
diff --git a/osu.Desktop/packages.config b/osu.Desktop/packages.config
index 6b6361b578..4757b50c4c 100644
--- a/osu.Desktop/packages.config
+++ b/osu.Desktop/packages.config
@@ -6,7 +6,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
-
+
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
index b90a06b94e..a617b65676 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
@@ -6,6 +6,7 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index 8e496c3b0c..7d0d80a0ce 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Catch.Objects
StartTime = lastTickTime,
ComboColour = ComboColour,
X = Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
- Samples = new SampleInfoList(Samples.Select(s => new SampleInfo
+ Samples = new List(Samples.Select(s => new SampleInfo
{
Bank = s.Bank,
Name = @"slidertick",
@@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Catch.Objects
StartTime = repeatStartTime + t,
ComboColour = ComboColour,
X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
- Samples = new SampleInfoList(Samples.Select(s => new SampleInfo
+ Samples = new List(Samples.Select(s => new SampleInfo
{
Bank = s.Bank,
Name = @"slidertick",
@@ -147,7 +147,7 @@ namespace osu.Game.Rulesets.Catch.Objects
set { Curve.ControlPoints = value; }
}
- public List RepeatSamples { get; set; } = new List();
+ public List> RepeatSamples { get; set; } = new List>();
public CurveType CurveType
{
diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
index 3826fd1129..3b9eacde9c 100644
--- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
@@ -5,7 +5,6 @@ using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
-using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
index b03c8d2eea..16c909e063 100644
--- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
+++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
@@ -36,8 +36,8 @@
$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll
True
-
- $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
+
+ $(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll
True
diff --git a/osu.Game.Rulesets.Catch/packages.config b/osu.Game.Rulesets.Catch/packages.config
index cde428acea..b39a85a382 100644
--- a/osu.Game.Rulesets.Catch/packages.config
+++ b/osu.Game.Rulesets.Catch/packages.config
@@ -1,5 +1,5 @@
-
+
\ 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 d5a799b4ed..407d4db143 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
@@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
///
/// The time to retrieve the sample info list from.
///
- private SampleInfoList sampleInfoListAt(double time)
+ private List sampleInfoListAt(double time)
{
var curveData = HitObject as IHasCurve;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
index 270c264e0c..8251dea5f7 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.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 System.Linq;
using osu.Game.Audio;
using osu.Game.Beatmaps;
@@ -435,7 +436,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
///
/// The time to retrieve the sample info list from.
///
- private SampleInfoList sampleInfoListAt(double time)
+ private List sampleInfoListAt(double time)
{
var curveData = HitObject as IHasCurve;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
index 3e9fc1ae27..8e832960df 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.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.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Objects;
@@ -77,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
};
if (hold.Head.Samples == null)
- hold.Head.Samples = new SampleInfoList();
+ hold.Head.Samples = new List();
hold.Head.Samples.Add(new SampleInfo { Name = SampleInfo.HIT_NORMAL });
diff --git a/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs b/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs
index d7bfa9caa1..ded1bc17af 100644
--- a/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs
+++ b/osu.Game.Rulesets.Mania/Judgements/HitWindows.cs
@@ -2,7 +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;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Judgements
{
diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs
index a8d1b079eb..e369df6ae1 100644
--- a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs
+++ b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.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 osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Judgements
{
@@ -24,4 +24,4 @@ namespace osu.Game.Rulesets.Mania.Judgements
}
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs
index d326c6fc0a..4787a4977b 100644
--- a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs
+++ b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.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 osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Judgements
{
@@ -11,4 +11,4 @@ namespace osu.Game.Rulesets.Mania.Judgements
protected override int NumericResultFor(HitResult result) => 20;
}
-}
\ 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 1f3b352da4..4762a98c40 100644
--- a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs
+++ b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs
@@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Judgements
{
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
index 7b207ca229..41d817a746 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
@@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Input.Bindings;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
index 557fbf6ea8..8ed5d2b924 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
@@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
@@ -113,4 +114,4 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
UpdateJudgement(true);
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
index 537246509b..aabfcafa85 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
@@ -8,6 +8,7 @@ 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;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
index 012137f555..140bab2225 100644
--- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
+++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
@@ -6,7 +6,6 @@ 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;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
diff --git a/osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs b/osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs
index 1932038411..b5890b289f 100644
--- a/osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs
+++ b/osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs
@@ -13,7 +13,7 @@ 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.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Timing;
using osu.Game.Tests.Visual;
diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
index 26181164f9..3393774a9c 100644
--- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
+++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
@@ -36,8 +36,8 @@
$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll
True
-
- $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
+
+ $(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll
True
diff --git a/osu.Game.Rulesets.Mania/packages.config b/osu.Game.Rulesets.Mania/packages.config
index cde428acea..b39a85a382 100644
--- a/osu.Game.Rulesets.Mania/packages.config
+++ b/osu.Game.Rulesets.Mania/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs
index 28b6a04376..cd9c3888df 100644
--- a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs
+++ b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs
@@ -4,7 +4,7 @@
using OpenTK;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Objects.Drawables;
-using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Judgements
{
@@ -34,4 +34,4 @@ namespace osu.Game.Rulesets.Osu.Judgements
public ComboResult Combo;
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuMod.cs b/osu.Game.Rulesets.Osu/Mods/OsuMod.cs
index 71349285b3..7b1f80f439 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuMod.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuMod.cs
@@ -11,8 +11,11 @@ using System.Collections.Generic;
using System.Linq;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.UI;
using OpenTK;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Framework.Graphics;
+using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Osu.Mods
{
@@ -23,13 +26,86 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModEasy : ModEasy
{
-
}
- public class OsuModHidden : ModHidden
+ public class OsuModHidden : ModHidden, IApplicableToDrawableHitObjects
{
public override string Description => @"Play with no approach circles and fading notes for a slight score advantage.";
public override double ScoreMultiplier => 1.06;
+
+ private const double fade_in_duration_multiplier = 0.4;
+ private const double fade_out_duration_multiplier = 0.3;
+
+ private float preEmpt => DrawableOsuHitObject.TIME_PREEMPT;
+
+ public void ApplyToDrawableHitObjects(IEnumerable drawables)
+ {
+ foreach (var d in drawables.OfType())
+ {
+ d.ApplyCustomUpdateState += ApplyHiddenState;
+ d.FadeInDuration = preEmpt * fade_in_duration_multiplier;
+ }
+ }
+
+ protected void ApplyHiddenState(DrawableHitObject drawable, ArmedState state)
+ {
+ if (!(drawable is DrawableOsuHitObject d))
+ return;
+
+ var fadeOutStartTime = d.HitObject.StartTime - preEmpt + d.FadeInDuration;
+ var fadeOutDuration = preEmpt * fade_out_duration_multiplier;
+
+ // new duration from completed fade in to end (before fading out)
+ var longFadeDuration = ((d.HitObject as IHasEndTime)?.EndTime ?? d.HitObject.StartTime) - fadeOutStartTime;
+
+ switch (drawable)
+ {
+ case DrawableHitCircle circle:
+ // we don't want to see the approach circle
+ circle.ApproachCircle.Hide();
+
+ // fade out immediately after fade in.
+ using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true))
+ circle.FadeOut(fadeOutDuration);
+ break;
+ case DrawableSlider slider:
+ using (slider.BeginAbsoluteSequence(fadeOutStartTime, true))
+ {
+ slider.Body.FadeOut(longFadeDuration, Easing.Out);
+
+ // delay a bit less to let the sliderball fade out peacefully instead of having a hard cut
+ using (slider.BeginDelayedSequence(longFadeDuration - fadeOutDuration, true))
+ slider.Ball.FadeOut(fadeOutDuration);
+ }
+
+ break;
+ case DrawableSpinner spinner:
+ // hide elements we don't care about.
+ spinner.Disc.Hide();
+ spinner.Ticks.Hide();
+ spinner.Background.Hide();
+
+ using (spinner.BeginAbsoluteSequence(fadeOutStartTime + longFadeDuration, true))
+ {
+ spinner.FadeOut(fadeOutDuration);
+
+ // speed up the end sequence accordingly
+ switch (state)
+ {
+ case ArmedState.Hit:
+ spinner.ScaleTo(spinner.Scale * 1.2f, fadeOutDuration * 2, Easing.Out);
+ break;
+ case ArmedState.Miss:
+ spinner.ScaleTo(spinner.Scale * 0.8f, fadeOutDuration * 2, Easing.In);
+ break;
+ }
+
+ spinner.Expire();
+ }
+
+ break;
+ }
+ }
}
public class OsuModHardRock : ModHardRock, IApplicableToHitObject
@@ -51,11 +127,6 @@ namespace osu.Game.Rulesets.Osu.Mods
slider.ControlPoints = newControlPoints;
slider.Curve?.Calculate(); // Recalculate the slider curve
}
-
- public void ApplyToHitObjects(RulesetContainer rulesetContainer)
- {
-
- }
}
public class OsuModSuddenDeath : ModSuddenDeath
@@ -96,7 +167,6 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModPerfect : ModPerfect
{
-
}
public class OsuModSpunOut : Mod
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index a973d2b580..6220bbd120 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -6,8 +6,8 @@ using osu.Framework.Graphics;
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;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly NumberPiece number;
private readonly GlowPiece glow;
- public DrawableHitCircle(OsuHitObject h) : base(h)
+ public DrawableHitCircle(HitCircle h) : base(h)
{
Origin = Anchor.Centre;
@@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
},
number = new NumberPiece
{
- Text = h is Spinner ? "S" : (HitObject.ComboIndex + 1).ToString(),
+ Text = (HitObject.ComboIndex + 1).ToString(),
},
ring = new RingPiece(),
flash = new FlashPiece(),
@@ -88,25 +88,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
base.UpdatePreemptState();
- ApproachCircle.FadeIn(Math.Min(TIME_FADEIN * 2, TIME_PREEMPT));
+ ApproachCircle.FadeIn(Math.Min(FadeInDuration * 2, TIME_PREEMPT));
ApproachCircle.ScaleTo(1.1f, TIME_PREEMPT);
}
protected override void UpdateCurrentState(ArmedState state)
{
- double duration = ((HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime) - HitObject.StartTime;
-
- glow.Delay(duration).FadeOut(400);
+ glow.FadeOut(400);
switch (state)
{
case ArmedState.Idle:
- this.Delay(duration + TIME_PREEMPT).FadeOut(TIME_FADEOUT);
+ this.Delay(TIME_PREEMPT).FadeOut(500);
+
Expire(true);
+
+ // override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early.
+ LifetimeEnd = HitObject.StartTime + HitObject.HitWindowFor(HitResult.Miss);
break;
case ArmedState.Miss:
ApproachCircle.FadeOut(50);
- this.FadeOut(TIME_FADEOUT / 5);
+ this.FadeOut(100);
Expire();
break;
case ArmedState.Hit:
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
index 3e0c23a1ab..f5f0300ae1 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
@@ -12,7 +12,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public const float TIME_PREEMPT = 600;
public const float TIME_FADEIN = 400;
- public const float TIME_FADEOUT = 500;
+
+ ///
+ /// The number of milliseconds used to fade in.
+ ///
+ public virtual double FadeInDuration { get; set; } = TIME_FADEIN;
+
+ public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Time.Current >= HitObject.StartTime - TIME_PREEMPT;
protected DrawableOsuHitObject(OsuHitObject hitObject)
: base(hitObject)
@@ -37,10 +43,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
- protected virtual void UpdatePreemptState()
- {
- this.FadeIn(TIME_FADEIN);
- }
+ protected virtual void UpdatePreemptState() => this.FadeIn(FadeInDuration);
protected virtual void UpdateCurrentState(ArmedState state)
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
index 7755a54e88..f16a41519e 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
@@ -2,10 +2,10 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
using OpenTK;
using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -24,4 +24,4 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.LoadComplete();
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
index a9b63ea642..477ced01c6 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
@@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
using osu.Game.Graphics;
using osu.Game.Rulesets.Osu.Judgements;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -24,19 +25,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
this.repeatPoint = repeatPoint;
this.drawableSlider = drawableSlider;
- AutoSizeAxes = Axes.Both;
+ Size = new Vector2(32 * repeatPoint.Scale);
+
Blending = BlendingMode.Additive;
Origin = Anchor.Centre;
- Scale = new Vector2(0.5f);
Children = new Drawable[]
{
new SpriteIcon
{
- Icon = FontAwesome.fa_eercast,
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Size = new Vector2(32),
+ RelativeSizeAxes = Axes.Both,
+ Icon = FontAwesome.fa_eercast
}
};
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index befe84e3e9..5a8bcae277 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -10,6 +10,7 @@ using System.Linq;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Framework.Graphics.Primitives;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -17,23 +18,24 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
private readonly Slider slider;
- private readonly DrawableHitCircle initialCircle;
+ public readonly DrawableHitCircle InitialCircle;
private readonly List components = new List();
private readonly Container ticks;
private readonly Container repeatPoints;
- private readonly SliderBody body;
- private readonly SliderBall ball;
+ public readonly SliderBody Body;
+ public readonly SliderBall Ball;
- public DrawableSlider(Slider s) : base(s)
+ public DrawableSlider(Slider s)
+ : base(s)
{
slider = s;
Children = new Drawable[]
{
- body = new SliderBody(s)
+ Body = new SliderBody(s)
{
AccentColour = AccentColour,
Position = s.StackedPosition,
@@ -41,16 +43,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
},
ticks = new Container(),
repeatPoints = new Container(),
- ball = new SliderBall(s)
+ Ball = new SliderBall(s)
{
Scale = new Vector2(s.Scale),
AccentColour = AccentColour,
AlwaysPresent = true,
Alpha = 0
},
- initialCircle = new DrawableHitCircle(new HitCircle
+ InitialCircle = new DrawableHitCircle(new HitCircle
{
- //todo: avoid creating this temporary HitCircle.
StartTime = s.StartTime,
Position = s.StackedPosition,
ComboIndex = s.ComboIndex,
@@ -61,16 +62,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
})
};
- components.Add(body);
- components.Add(ball);
+ components.Add(Body);
+ components.Add(Ball);
- AddNested(initialCircle);
+ AddNested(InitialCircle);
var repeatDuration = s.Curve.Distance / s.Velocity;
foreach (var tick in s.NestedHitObjects.OfType())
{
var repeatStartTime = s.StartTime + tick.RepeatIndex * repeatDuration;
- var fadeInTime = repeatStartTime + (tick.StartTime - repeatStartTime) / 2 - (tick.RepeatIndex == 0 ? TIME_FADEIN : TIME_FADEIN / 2);
+ var fadeInTime = repeatStartTime + (tick.StartTime - repeatStartTime) / 2 - (tick.RepeatIndex == 0 ? FadeInDuration : FadeInDuration / 2);
var fadeOutTime = repeatStartTime + repeatDuration;
var drawableTick = new DrawableSliderTick(tick)
@@ -87,7 +88,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
foreach (var repeatPoint in s.NestedHitObjects.OfType())
{
var repeatStartTime = s.StartTime + repeatPoint.RepeatIndex * repeatDuration;
- var fadeInTime = repeatStartTime + (repeatPoint.StartTime - repeatStartTime) / 2 - (repeatPoint.RepeatIndex == 0 ? TIME_FADEIN : TIME_FADEIN / 2);
+ var fadeInTime = repeatStartTime + (repeatPoint.StartTime - repeatStartTime) / 2 - (repeatPoint.RepeatIndex == 0 ? FadeInDuration : FadeInDuration / 2);
var fadeOutTime = repeatStartTime + repeatDuration;
var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this)
@@ -105,11 +106,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private int currentRepeat;
public bool Tracking;
+ public override double FadeInDuration
+ {
+ get { return base.FadeInDuration; }
+ set { InitialCircle.FadeInDuration = base.FadeInDuration = value; }
+ }
+
protected override void Update()
{
base.Update();
- Tracking = ball.Tracking;
+ Tracking = Ball.Tracking;
double progress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
@@ -117,18 +124,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
progress = slider.ProgressAt(progress);
if (repeat > currentRepeat)
- {
- if (repeat < slider.RepeatCount && ball.Tracking)
- PlaySamples();
currentRepeat = repeat;
- }
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
- if (!initialCircle.Judgements.Any(j => j.IsHit))
- initialCircle.Position = slider.Curve.PositionAt(progress);
+ 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;
+ foreach (var t in ticks.Children) t.Tracking = Ball.Tracking;
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
@@ -137,13 +140,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
var judgementsCount = ticks.Children.Count + repeatPoints.Children.Count + 1;
var judgementsHit = ticks.Children.Count(t => t.Judgements.Any(j => j.IsHit)) + repeatPoints.Children.Count(t => t.Judgements.Any(j => j.IsHit));
- if (initialCircle.Judgements.Any(j => j.IsHit))
+ if (InitialCircle.Judgements.Any(j => j.IsHit))
judgementsHit++;
var hitFraction = (double)judgementsHit / judgementsCount;
- if (hitFraction == 1 && initialCircle.Judgements.Any(j => j.Result == HitResult.Great))
+ 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))
+ else if (hitFraction >= 0.5 && InitialCircle.Judgements.Any(j => j.Result >= HitResult.Good))
AddJudgement(new OsuJudgement { Result = HitResult.Good });
else if (hitFraction > 0)
AddJudgement(new OsuJudgement { Result = HitResult.Meh });
@@ -154,26 +157,21 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateCurrentState(ArmedState state)
{
- ball.FadeIn();
+ Ball.FadeIn();
using (BeginDelayedSequence(slider.Duration, true))
{
- body.FadeOut(160);
- ball.FadeOut(160);
+ Body.FadeOut(160);
+ Ball.FadeOut(160);
this.FadeOut(800)
.Expire();
}
}
- public Drawable ProxiedLayer => initialCircle.ApproachCircle;
+ public Drawable ProxiedLayer => InitialCircle.ApproachCircle;
- public override Vector2 SelectionPoint => ToScreenSpace(body.Position);
- public override Quad SelectionQuad => body.PathDrawQuad;
- }
-
- internal interface ISliderProgress
- {
- void UpdateProgress(double progress, int repeat);
+ public override Vector2 SelectionPoint => ToScreenSpace(Body.Position);
+ public override Quad SelectionQuad => Body.PathDrawQuad;
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
index 7199691ae6..bce7ef6141 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
@@ -8,6 +8,7 @@ using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Osu.Judgements;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index 054a2067ec..5351ad50c4 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -13,6 +13,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Screens.Ranking;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -20,13 +21,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
private readonly Spinner spinner;
- private readonly SpinnerDisc disc;
- private readonly SpinnerTicks ticks;
+ public readonly SpinnerDisc Disc;
+ public readonly SpinnerTicks Ticks;
private readonly SpinnerSpmCounter spmCounter;
private readonly Container mainContainer;
- private readonly SpinnerBackground background;
+ public readonly SpinnerBackground Background;
private readonly Container circleContainer;
private readonly CirclePiece circle;
private readonly GlowPiece glow;
@@ -84,20 +85,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
RelativeSizeAxes = Axes.Y,
Children = new Drawable[]
{
- background = new SpinnerBackground
+ Background = new SpinnerBackground
{
Alpha = 0.6f,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
- disc = new SpinnerDisc(spinner)
+ Disc = new SpinnerDisc(spinner)
{
Scale = Vector2.Zero,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
circleContainer.CreateProxy(),
- ticks = new SpinnerTicks
+ Ticks = new SpinnerTicks
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -114,22 +115,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
};
}
- public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1);
+ public float Progress => MathHelper.Clamp(Disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1);
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (Time.Current < HitObject.StartTime) return;
- if (Progress >= 1 && !disc.Complete)
+ if (Progress >= 1 && !Disc.Complete)
{
- disc.Complete = true;
+ Disc.Complete = true;
const float duration = 200;
- disc.FadeAccent(completeColour, duration);
+ Disc.FadeAccent(completeColour, duration);
- background.FadeAccent(completeColour, duration);
- background.FadeOut(duration);
+ Background.FadeAccent(completeColour, duration);
+ Background.FadeOut(duration);
circle.FadeColour(completeColour, duration);
glow.FadeColour(completeColour, duration);
@@ -153,20 +154,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
normalColour = baseColour;
- background.AccentColour = normalColour;
+ Background.AccentColour = normalColour;
completeColour = colours.YellowLight.Opacity(0.75f);
- disc.AccentColour = fillColour;
+ Disc.AccentColour = fillColour;
circle.Colour = colours.BlueDark;
glow.Colour = colours.BlueDark;
}
protected override void Update()
{
- disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton);
- if (!spmCounter.IsPresent && disc.Tracking)
- spmCounter.FadeIn(TIME_FADEIN);
+ Disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton);
+ if (!spmCounter.IsPresent && Disc.Tracking)
+ spmCounter.FadeIn(FadeInDuration);
base.Update();
}
@@ -175,14 +176,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
base.UpdateAfterChildren();
- circle.Rotation = disc.Rotation;
- ticks.Rotation = disc.Rotation;
- spmCounter.SetRotation(disc.RotationAbsolute);
+ circle.Rotation = Disc.Rotation;
+ Ticks.Rotation = Disc.Rotation;
+ spmCounter.SetRotation(Disc.RotationAbsolute);
float relativeCircleScale = spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight;
- disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint);
+ Disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint);
- symbol.RotateTo(disc.Rotation / 2, 500, Easing.OutQuint);
+ symbol.RotateTo(Disc.Rotation / 2, 500, Easing.OutQuint);
}
protected override void UpdatePreemptState()
@@ -192,7 +193,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
circleContainer.ScaleTo(spinner.Scale * 0.3f);
circleContainer.ScaleTo(spinner.Scale, TIME_PREEMPT / 1.4f, Easing.OutQuint);
- disc.RotateTo(-720);
+ Disc.RotateTo(-720);
symbol.RotateTo(-720);
mainContainer
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
index ca75a61738..9f54ce3fa3 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
@@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private const float idle_alpha = 0.2f;
private const float tracking_alpha = 0.4f;
+ public override bool IsPresent => true; // handle input when hidden
+
public SpinnerDisc(Spinner s)
{
spinner = s;
diff --git a/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs b/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs
new file mode 100644
index 0000000000..cb0d177a60
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs
@@ -0,0 +1,10 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Rulesets.Osu.Objects
+{
+ public interface ISliderProgress
+ {
+ void UpdateProgress(double progress, int repeat);
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index 7532387aa2..a3a6527b31 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -7,7 +7,7 @@ using OpenTK;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
using osu.Game.Beatmaps.ControlPoints;
-using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 4f5a44e61d..ec51a10345 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Objects
///
internal float LazyTravelDistance;
- public List RepeatSamples { get; set; } = new List();
+ public List> RepeatSamples { get; set; } = new List>();
public int RepeatCount { get; set; } = 1;
private int stackHeight;
@@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Osu.Objects
StackHeight = StackHeight,
Scale = Scale,
ComboColour = ComboColour,
- Samples = new SampleInfoList(Samples.Select(s => new SampleInfo
+ Samples = new List(Samples.Select(s => new SampleInfo
{
Bank = s.Bank,
Name = @"slidertick",
@@ -151,28 +151,22 @@ namespace osu.Game.Rulesets.Osu.Objects
private void createRepeatPoints()
{
- var length = Curve.Distance;
- var repeatPointDistance = Math.Min(Distance, length);
- var repeatDuration = length / Velocity;
+ var repeatDuration = Distance / Velocity;
for (var repeat = 1; repeat < RepeatCount; repeat++)
{
- for (var d = repeatPointDistance; d <= length; d += repeatPointDistance)
- {
- var repeatStartTime = StartTime + repeat * repeatDuration;
- var distanceProgress = d / length;
+ var repeatStartTime = StartTime + repeat * repeatDuration;
- AddNested(new RepeatPoint
- {
- RepeatIndex = repeat,
- StartTime = repeatStartTime,
- Position = Curve.PositionAt(distanceProgress),
- StackHeight = StackHeight,
- Scale = Scale,
- ComboColour = ComboColour,
- Samples = new SampleInfoList(RepeatSamples[repeat])
- });
- }
+ AddNested(new RepeatPoint
+ {
+ RepeatIndex = repeat,
+ StartTime = repeatStartTime,
+ Position = Curve.PositionAt(repeat % 2),
+ StackHeight = StackHeight,
+ Scale = Scale,
+ ComboColour = ComboColour,
+ Samples = new List(RepeatSamples[repeat])
+ });
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
index f82c6ce3b2..ba774e887f 100644
--- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
+++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
@@ -9,9 +9,9 @@ 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;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Replays
{
diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs
index 2cf321da50..38c602bc42 100644
--- a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs
@@ -41,10 +41,10 @@ namespace osu.Game.Rulesets.Osu.Scoring
mods = Score.Mods;
accuracy = Score.Accuracy;
scoreMaxCombo = Score.MaxCombo;
- count300 = Convert.ToInt32(Score.Statistics["300"]);
- count100 = Convert.ToInt32(Score.Statistics["100"]);
- count50 = Convert.ToInt32(Score.Statistics["50"]);
- countMiss = Convert.ToInt32(Score.Statistics["x"]);
+ count300 = Convert.ToInt32(Score.Statistics[HitResult.Great]);
+ count100 = Convert.ToInt32(Score.Statistics[HitResult.Good]);
+ count50 = Convert.ToInt32(Score.Statistics[HitResult.Meh]);
+ countMiss = Convert.ToInt32(Score.Statistics[HitResult.Miss]);
// Don't count scores made with supposedly unranked mods
if (mods.Any(m => !m.Ranked))
diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
index ad9737af52..7520e1801c 100644
--- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
@@ -6,7 +6,6 @@ using System.Linq;
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;
using osu.Game.Rulesets.Osu.Objects.Drawables;
@@ -33,8 +32,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
foreach (var obj in beatmap.HitObjects)
{
- var slider = obj as Slider;
- if (slider != null)
+ if (obj is Slider slider)
{
// Head
AddJudgement(new OsuJudgement { Result = HitResult.Great });
@@ -64,10 +62,10 @@ namespace osu.Game.Rulesets.Osu.Scoring
{
base.PopulateScore(score);
- 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);
+ score.Statistics[HitResult.Great] = scoreResultCounts.GetOrDefault(HitResult.Great);
+ score.Statistics[HitResult.Good] = scoreResultCounts.GetOrDefault(HitResult.Good);
+ score.Statistics[HitResult.Meh] = scoreResultCounts.GetOrDefault(HitResult.Meh);
+ score.Statistics[HitResult.Miss] = scoreResultCounts.GetOrDefault(HitResult.Miss);
}
protected override void OnNewJudgement(Judgement judgement)
diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs
new file mode 100644
index 0000000000..cdce19ad21
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs
@@ -0,0 +1,116 @@
+// 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.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Tests.Visual;
+using OpenTK;
+using osu.Game.Rulesets.Osu.Mods;
+using OpenTK.Graphics;
+using osu.Game.Rulesets.Osu.Judgements;
+using System.Collections.Generic;
+using System;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ [Ignore("getting CI working")]
+ public class TestCaseHitCircle : OsuTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(HitCircle),
+ typeof(OsuModHidden),
+ typeof(DrawableHitCircle)
+ };
+
+ private readonly Container content;
+ protected override Container Content => content;
+
+ private bool auto;
+ private bool hidden;
+ private int depthIndex;
+ private int circleSize;
+ private float circleScale = 1;
+
+ public TestCaseHitCircle()
+ {
+ base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 }));
+
+ AddStep("Single", () => testSingle());
+ AddStep("Stream", testStream);
+ AddToggleStep("Auto", v => auto = v);
+ AddToggleStep("Hidden", v => hidden = v);
+ AddSliderStep("CircleSize", 0, 10, 0, s => circleSize = s);
+ AddSliderStep("CircleScale", 0.5f, 2, 1, s => circleScale = s);
+ }
+
+ private void testSingle(double timeOffset = 0, Vector2? positionOffset = null)
+ {
+ positionOffset = positionOffset ?? Vector2.Zero;
+
+ var circle = new HitCircle
+ {
+ StartTime = Time.Current + 1000 + timeOffset,
+ Position = positionOffset.Value,
+ ComboColour = Color4.LightSeaGreen
+ };
+
+ circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize });
+
+ var drawable = new TestDrawableHitCircle(circle, auto)
+ {
+ Anchor = Anchor.Centre,
+ Scale = new Vector2(circleScale),
+ Depth = depthIndex++
+ };
+
+ if (auto)
+ drawable.State.Value = ArmedState.Hit;
+
+ if (hidden)
+ new OsuModHidden().ApplyToDrawableHitObjects(new [] { drawable });
+
+ Add(drawable);
+ }
+
+ private void testStream()
+ {
+ Vector2 pos = Vector2.Zero;
+
+ for (int i = 0; i <= 1000; i += 100)
+ {
+ testSingle(i, pos);
+ pos += new Vector2(10);
+ }
+ }
+
+ private class TestDrawableHitCircle : DrawableHitCircle
+ {
+ private readonly bool auto;
+
+ public TestDrawableHitCircle(HitCircle h, bool auto) : base(h)
+ {
+ this.auto = auto;
+ }
+
+ protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ {
+ if (auto && !userTriggered && timeOffset > 0)
+ {
+ // pretend we really hit it
+ AddJudgement(new OsuJudgement
+ {
+ Result = HitObject.ScoreResultForOffset(timeOffset)
+ });
+ }
+ base.CheckForJudgements(userTriggered, timeOffset);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs
deleted file mode 100644
index c4932d7803..0000000000
--- a/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs
+++ /dev/null
@@ -1,129 +0,0 @@
-// 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 NUnit.Framework;
-using osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Timing;
-using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Rulesets.Osu.Objects.Drawables;
-using osu.Game.Tests.Visual;
-using OpenTK;
-
-namespace osu.Game.Rulesets.Osu.Tests
-{
- [TestFixture]
- [Ignore("getting CI working")]
- public class TestCaseHitObjects : OsuTestCase
- {
- private FramedClock framedClock;
-
- private bool auto;
-
- [BackgroundDependencyLoader]
- private void load(RulesetStore rulesets)
- {
- var rateAdjustClock = new StopwatchClock(true);
- framedClock = new FramedClock(rateAdjustClock);
-
- AddStep(@"circles", () => loadHitobjects(HitObjectType.Circle));
- AddStep(@"slider", () => loadHitobjects(HitObjectType.Slider));
- AddStep(@"spinner", () => loadHitobjects(HitObjectType.Spinner));
-
- AddToggleStep("Auto", state => { auto = state; loadHitobjects(mode); });
- AddSliderStep("Playback speed", 0.0, 2.0, 0.5, v => rateAdjustClock.Rate = v);
-
- framedClock.ProcessFrame();
-
- var clockAdjustContainer = new Container
- {
- RelativeSizeAxes = Axes.Both,
- Clock = framedClock,
- Children = new[]
- {
- playfieldContainer = new OsuInputManager(rulesets.GetRuleset(0)) { RelativeSizeAxes = Axes.Both },
- approachContainer = new Container { RelativeSizeAxes = Axes.Both }
- }
- };
-
- Add(clockAdjustContainer);
- }
-
- private HitObjectType mode = HitObjectType.Slider;
-
- private Container playfieldContainer;
- private Container approachContainer;
-
- private void loadHitobjects(HitObjectType mode)
- {
- this.mode = mode;
-
- switch (mode)
- {
- case HitObjectType.Circle:
- const int count = 10;
-
- for (int i = 0; i < count; i++)
- {
- var h = new HitCircle
- {
- StartTime = framedClock.CurrentTime + 600 + i * 80,
- Position = new Vector2((i - count / 2) * 14),
- };
-
- add(new DrawableHitCircle(h));
- }
- break;
- case HitObjectType.Slider:
- add(new DrawableSlider(new Slider
- {
- StartTime = framedClock.CurrentTime + 600,
- ControlPoints = new List
- {
- new Vector2(-200, 0),
- new Vector2(400, 0),
- },
- Distance = 400,
- Position = new Vector2(-200, 0),
- Velocity = 1,
- TickDistance = 100,
- }));
- break;
- case HitObjectType.Spinner:
- add(new DrawableSpinner(new Spinner
- {
- StartTime = framedClock.CurrentTime + 600,
- EndTime = framedClock.CurrentTime + 1600,
- Position = new Vector2(0, 0),
- }));
- break;
- }
- }
-
- private int depth;
-
- private void add(DrawableOsuHitObject h)
- {
- h.Anchor = Anchor.Centre;
- h.Depth = depth++;
-
- if (auto)
- h.State.Value = ArmedState.Hit;
-
- playfieldContainer.Add(h);
- var proxyable = h as IDrawableHitObjectWithProxiedApproach;
- if (proxyable != null)
- approachContainer.Add(proxyable.ProxiedLayer.CreateProxy());
- }
-
- private enum HitObjectType
- {
- Circle,
- Slider,
- Spinner
- }
- }
-}
diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs
new file mode 100644
index 0000000000..5b6b357351
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs
@@ -0,0 +1,146 @@
+// 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 NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Audio;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Tests.Visual;
+using OpenTK;
+using osu.Game.Rulesets.Osu.Mods;
+using OpenTK.Graphics;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ [Ignore("getting CI working")]
+ public class TestCaseSlider : OsuTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(Slider),
+ typeof(HitCircle),
+ typeof(OsuModHidden),
+ typeof(DrawableSlider),
+ typeof(DrawableHitCircle),
+ typeof(DrawableSliderTick),
+ typeof(DrawableRepeatPoint)
+ };
+
+ private readonly Container content;
+ protected override Container Content => content;
+
+ private bool hidden;
+ private int repeats;
+ private int depthIndex;
+ private int circleSize;
+ private float circleScale = 1;
+ private double speedMultiplier = 2;
+ private double sliderMultiplier = 2;
+
+ public TestCaseSlider()
+ {
+ base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 }));
+
+ AddStep("Single", () => testSingle());
+ AddStep("Stream", testStream);
+ AddStep("Repeated", () => testRepeated(repeats));
+ AddToggleStep("Hidden", v => hidden = v);
+ AddSliderStep("Repeats", 1, 10, 1, s => repeats = s);
+ AddSliderStep("CircleSize", 0, 10, 0, s => circleSize = s);
+ AddSliderStep("CircleScale", 0.5f, 2, 1, s => circleScale = s);
+ AddSliderStep("SpeedMultiplier", 0.1, 10, 2, s => speedMultiplier = s);
+ AddSliderStep("SliderMultiplier", 0.1, 10, 2, s => sliderMultiplier = s);
+ }
+
+ private void testSingle(double timeOffset = 0, Vector2? positionOffset = null)
+ {
+ positionOffset = positionOffset ?? Vector2.Zero;
+
+ var slider = new Slider
+ {
+ StartTime = Time.Current + 1000 + timeOffset,
+ Position = new Vector2(-200, 0) + positionOffset.Value,
+ ComboColour = Color4.LightSeaGreen,
+ ControlPoints = new List
+ {
+ new Vector2(-200, 0) + positionOffset.Value,
+ new Vector2(400, 0) + positionOffset.Value,
+ },
+ Distance = 400
+ };
+
+ addSlider(slider);
+ }
+
+ private void testRepeated(int repeats)
+ {
+ // The first run through the slider is considered a repeat
+ repeats++;
+
+ var repeatSamples = new List>();
+ for (int i = 0; i < repeats; i++)
+ repeatSamples.Add(new List());
+
+ var slider = new Slider
+ {
+ StartTime = Time.Current + 1000,
+ Position = new Vector2(-200, 0),
+ ComboColour = Color4.LightSeaGreen,
+ ControlPoints = new List
+ {
+ new Vector2(-200, 0),
+ new Vector2(400, 0),
+ },
+ Distance = 400,
+ RepeatCount = repeats,
+ RepeatSamples = repeatSamples
+ };
+
+ addSlider(slider);
+ }
+
+ private void testStream()
+ {
+ Vector2 pos = Vector2.Zero;
+
+ for (int i = 0; i <= 1000; i += 100)
+ {
+ testSingle(i, pos);
+ pos += new Vector2(10);
+ }
+ }
+
+ private void addSlider(Slider slider)
+ {
+ var cpi = new ControlPointInfo();
+ cpi.DifficultyPoints.Add(new DifficultyControlPoint { SpeedMultiplier = speedMultiplier });
+
+ var difficulty = new BeatmapDifficulty
+ {
+ SliderMultiplier = (float)sliderMultiplier,
+ CircleSize = circleSize
+ };
+
+ slider.ApplyDefaults(cpi, difficulty);
+
+ var drawable = new DrawableSlider(slider)
+ {
+ Anchor = Anchor.Centre,
+ Scale = new Vector2(circleScale),
+ Depth = depthIndex++
+ };
+
+ if (hidden)
+ new OsuModHidden().ApplyToDrawableHitObjects(new [] { drawable });
+
+ Add(drawable);
+ }
+ }
+
+}
diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs
new file mode 100644
index 0000000000..c4ee56455a
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs
@@ -0,0 +1,66 @@
+// 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 NUnit.Framework;
+using OpenTK;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ [Ignore("getting CI working")]
+ public class TestCaseSpinner : OsuTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+{
+ typeof(Spinner),
+ typeof(OsuModHidden),
+ typeof(DrawableSpinner)
+ };
+
+ private readonly Container content;
+ protected override Container Content => content;
+
+ private bool hidden;
+ private int depthIndex;
+ private int circleSize;
+ private float circleScale = 1;
+
+ public TestCaseSpinner()
+ {
+ base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 }));
+
+ AddStep("Single", testSingle);
+ AddToggleStep("Hidden", v => hidden = v);
+ AddSliderStep("CircleSize", 0, 10, 0, s => circleSize = s);
+ AddSliderStep("CircleScale", 0.5f, 2, 1, s => circleScale = s);
+ }
+
+ private void testSingle()
+ {
+ var spinner = new Spinner { StartTime = Time.Current + 1000, EndTime = Time.Current + 4000 };
+
+ spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize });
+
+ var drawable = new DrawableSpinner(spinner)
+ {
+ Anchor = Anchor.Centre,
+ Scale = new Vector2(circleScale),
+ Depth = depthIndex++
+ };
+
+ if (hidden)
+ new OsuModHidden().ApplyToDrawableHitObjects(new [] { drawable });
+
+ Add(drawable);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
index 7c9cbd63fc..f37b87e533 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
@@ -35,16 +35,13 @@ namespace osu.Game.Rulesets.Osu.UI
protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h)
{
- var circle = h as HitCircle;
- if (circle != null)
+ if (h is HitCircle circle)
return new DrawableHitCircle(circle);
- var slider = h as Slider;
- if (slider != null)
+ if (h is Slider slider)
return new DrawableSlider(slider);
- var spinner = h as Spinner;
- if (spinner != null)
+ if (h is Spinner spinner)
return new DrawableSpinner(spinner);
return null;
}
diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
index ec71869adb..785c3e17fb 100644
--- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
+++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
@@ -37,8 +37,8 @@
$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll
True
-
- $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
+
+ $(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll
True
@@ -75,6 +75,7 @@
+
@@ -86,8 +87,10 @@
-
+
+
+
diff --git a/osu.Game.Rulesets.Osu/packages.config b/osu.Game.Rulesets.Osu/packages.config
index cde428acea..b39a85a382 100644
--- a/osu.Game.Rulesets.Osu/packages.config
+++ b/osu.Game.Rulesets.Osu/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs
new file mode 100644
index 0000000000..982b339d3a
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs
@@ -0,0 +1,46 @@
+// 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.Audio;
+using osu.Framework.Audio.Sample;
+using osu.Game.Audio;
+using osu.Game.Beatmaps.ControlPoints;
+
+namespace osu.Game.Rulesets.Taiko.Audio
+{
+ public class DrumSampleMapping
+ {
+ private readonly ControlPointInfo controlPoints;
+ private readonly Dictionary mappings = new Dictionary();
+
+ public DrumSampleMapping(ControlPointInfo controlPoints, AudioManager audio)
+ {
+ this.controlPoints = controlPoints;
+
+ IEnumerable samplePoints;
+ if (controlPoints.SamplePoints.Count == 0)
+ // Get the default sample point
+ samplePoints = new[] { controlPoints.SamplePointAt(double.MinValue) };
+ else
+ samplePoints = controlPoints.SamplePoints;
+
+ foreach (var s in samplePoints)
+ {
+ mappings[s.Time] = new DrumSample
+ {
+ Centre = s.GetSampleInfo().GetChannel(audio.Sample, "Taiko"),
+ Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample, "Taiko")
+ };
+ }
+ }
+
+ public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(time).Time];
+
+ public class DrumSample
+ {
+ public SampleChannel Centre;
+ public SampleChannel Rim;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
index 9b4a6c47a9..690b80b12c 100644
--- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
@@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
var curveData = obj as IHasCurve;
// Old osu! used hit sounding to determine various hit type information
- SampleInfoList samples = obj.Samples;
+ List samples = obj.Samples;
bool strong = samples.Any(s => s.Name == SampleInfo.HIT_FINISH);
@@ -115,12 +115,12 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
{
- List allSamples = curveData != null ? curveData.RepeatSamples : new List(new[] { samples });
+ List> allSamples = curveData != null ? curveData.RepeatSamples : new List>(new[] { samples });
int i = 0;
for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)
{
- SampleInfoList currentSamples = allSamples[i];
+ List currentSamples = allSamples[i];
bool isRim = currentSamples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);
strong = currentSamples.Any(s => s.Name == SampleInfo.HIT_FINISH);
diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs
index c9daef8c99..ce5be8d148 100644
--- a/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.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 osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Taiko.Judgements
{
@@ -20,4 +20,4 @@ namespace osu.Game.Rulesets.Taiko.Judgements
}
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs
index 3cd134f3f7..70cdd1fe0e 100644
--- a/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs
@@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Taiko.Judgements
{
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
index 75e988ced6..f5bafefd0b 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
@@ -13,6 +13,7 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
index e662f61bbe..a741e35963 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
@@ -4,6 +4,7 @@
using System;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
index df33aae94e..0c10c7142e 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
@@ -5,6 +5,7 @@ using System;
using System.Linq;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
@@ -68,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
validKeyPressed = HitActions.Contains(action);
// Only count this as handled if the new judgement is a hit
- return UpdateJudgement(true) && Judgements.LastOrDefault()?.IsHit == true;
+ return UpdateJudgement(true);
}
protected override void Update()
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs
index 81b23af1a9..249bb41d91 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs
@@ -3,7 +3,7 @@
using System;
using System.Linq;
-using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
@@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (!userTriggered)
{
if (timeOffset > second_hit_window)
- AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Miss });
+ AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.None });
return;
}
@@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
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) && Judgements.LastOrDefault()?.IsHit == true;
+ return firstKeyHeld && UpdateJudgement(true);
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
index 738902846b..26e6585fb9 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
@@ -14,6 +14,7 @@ using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Taiko.Judgements;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
@@ -34,10 +35,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
private readonly CircularContainer targetRing;
private readonly CircularContainer expandingRing;
- 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.
///
@@ -205,19 +202,20 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
}
}
+ private bool? lastWasCentre;
+
public override bool OnPressed(TaikoAction action)
{
// 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 = rimActions.Contains(action) ? rimActions : centreActions;
+ var isCentre = action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre;
- // Ensure alternating keysets
- if (keySet == lastAction)
+ // Ensure alternating centre and rim hits
+ if (lastWasCentre == isCentre)
return false;
- lastAction = keySet;
+ lastWasCentre = isCentre;
UpdateJudgement(true);
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
index 7976cbbbc1..cc7dd2fa0f 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
@@ -6,6 +6,9 @@ using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using OpenTK;
+using System.Linq;
+using osu.Game.Audio;
+using System.Collections.Generic;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
@@ -35,6 +38,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
MainPiece.KiaiMode = HitObject.Kiai;
}
+ // Normal and clap samples are handled by the drum
+ protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != SampleInfo.HIT_NORMAL && s.Name != SampleInfo.HIT_CLAP);
+
+ protected override string SampleNamespace => "Taiko";
+
protected virtual TaikoPiece CreateMainPiece() => new CirclePiece();
public abstract bool OnPressed(TaikoAction action);
diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
index 4104b59979..a39d627cc4 100644
--- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
@@ -3,8 +3,6 @@
using osu.Game.Rulesets.Objects.Types;
using System;
-using System.Linq;
-using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
@@ -74,13 +72,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
FirstTick = first,
TickSpacing = tickSpacing,
StartTime = t,
- IsStrong = IsStrong,
- Samples = new SampleInfoList(Samples.Select(s => new SampleInfo
- {
- Bank = s.Bank,
- Name = @"slidertick",
- Volume = s.Volume
- }))
+ IsStrong = IsStrong
});
first = false;
diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs
index f74a543ca9..cd2876ea2d 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Swell.cs
@@ -16,4 +16,4 @@ namespace osu.Game.Rulesets.Taiko.Objects
///
public int RequiredHits = 10;
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs
index df1a19267f..c43899ebf1 100644
--- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs
+++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs
@@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
{
foreach (var tick in drumRoll.NestedHitObjects.OfType())
{
- Frames.Add(new ReplayFrame(tick.StartTime, null, null, hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2));
+ Frames.Add(new ReplayFrame(tick.StartTime, null, null, hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2));
hitButton = !hitButton;
}
}
diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
index df9ce5e2eb..3848e36fc9 100644
--- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
+++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
@@ -4,7 +4,6 @@
using System.Linq;
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;
diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs b/osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs
new file mode 100644
index 0000000000..172c6e9d84
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs
@@ -0,0 +1,44 @@
+// 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 NUnit.Framework;
+using OpenTK;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Audio;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Taiko.Audio;
+using osu.Game.Rulesets.Taiko.UI;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Taiko.Tests
+{
+ [Ignore("getting CI working")]
+ public class TestCaseInputDrum : OsuTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(InputDrum),
+ typeof(DrumSampleMapping),
+ typeof(SampleInfo),
+ typeof(SampleControlPoint)
+ };
+
+ public TestCaseInputDrum()
+ {
+ Add(new TaikoInputManager(new RulesetInfo { ID = 1 })
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(200),
+ Child = new InputDrum(new ControlPointInfo())
+ }
+ });
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs
index bca4806108..1f13864c2a 100644
--- a/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs
@@ -20,6 +20,7 @@ using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Visual;
using OpenTK;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Taiko.Tests
{
@@ -165,11 +166,15 @@ namespace osu.Game.Rulesets.Taiko.Tests
private void addSwell(double duration = default_duration)
{
- rulesetContainer.Playfield.Add(new DrawableSwell(new Swell
+ var swell = new Swell
{
StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
Duration = duration,
- }));
+ };
+
+ swell.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+
+ rulesetContainer.Playfield.Add(new DrawableSwell(swell));
}
private void addDrumRoll(bool strong, double duration = default_duration)
@@ -184,6 +189,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
Duration = duration,
};
+ d.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+
rulesetContainer.Playfield.Add(new DrawableDrumRoll(d));
}
@@ -195,6 +202,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
IsStrong = strong
};
+ h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+
if (strong)
rulesetContainer.Playfield.Add(new DrawableCentreHitStrong(h));
else
@@ -209,6 +218,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
IsStrong = strong
};
+ h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+
if (strong)
rulesetContainer.Playfield.Add(new DrawableRimHitStrong(h));
else
diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
index 41b66c286b..0b67613ec3 100644
--- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
@@ -6,6 +6,7 @@ using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Judgements;
using osu.Framework.Graphics;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Taiko.UI
{
@@ -49,4 +50,4 @@ namespace osu.Game.Rulesets.Taiko.UI
base.LoadComplete();
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
index 5e79166bec..9b2ea095d2 100644
--- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
+++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
@@ -4,12 +4,15 @@
using System;
using OpenTK;
using osu.Framework.Allocation;
+using osu.Framework.Audio;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Bindings;
+using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
+using osu.Game.Rulesets.Taiko.Audio;
namespace osu.Game.Rulesets.Taiko.UI
{
@@ -18,16 +21,26 @@ namespace osu.Game.Rulesets.Taiko.UI
///
internal class InputDrum : Container
{
- public InputDrum()
+ private const float middle_split = 0.025f;
+
+ private readonly ControlPointInfo controlPoints;
+
+ public InputDrum(ControlPointInfo controlPoints)
{
+ this.controlPoints = controlPoints;
+
RelativeSizeAxes = Axes.Both;
FillMode = FillMode.Fit;
+ }
- const float middle_split = 0.025f;
+ [BackgroundDependencyLoader]
+ private void load(AudioManager audio)
+ {
+ var sampleMappings = new DrumSampleMapping(controlPoints, audio);
Children = new Drawable[]
{
- new TaikoHalfDrum(false)
+ new TaikoHalfDrum(false, sampleMappings)
{
Name = "Left Half",
Anchor = Anchor.Centre,
@@ -38,7 +51,7 @@ namespace osu.Game.Rulesets.Taiko.UI
RimAction = TaikoAction.LeftRim,
CentreAction = TaikoAction.LeftCentre
},
- new TaikoHalfDrum(true)
+ new TaikoHalfDrum(true, sampleMappings)
{
Name = "Right Half",
Anchor = Anchor.Centre,
@@ -72,8 +85,12 @@ namespace osu.Game.Rulesets.Taiko.UI
private readonly Sprite centre;
private readonly Sprite centreHit;
- public TaikoHalfDrum(bool flipped)
+ private readonly DrumSampleMapping sampleMappings;
+
+ public TaikoHalfDrum(bool flipped, DrumSampleMapping sampleMappings)
{
+ this.sampleMappings = sampleMappings;
+
Masking = true;
Children = new Drawable[]
@@ -128,15 +145,21 @@ namespace osu.Game.Rulesets.Taiko.UI
Drawable target = null;
Drawable back = null;
+ var drumSample = sampleMappings.SampleAt(Time.Current);
+
if (action == CentreAction)
{
target = centreHit;
back = centre;
+
+ drumSample.Centre?.Play();
}
else if (action == RimAction)
{
target = rimHit;
back = rim;
+
+ drumSample.Rim?.Play();
}
if (target != null)
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
index 2cc95fc981..3fdbd056ce 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
@@ -16,17 +16,11 @@ using osu.Framework.Extensions.Color4Extensions;
using System.Linq;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
-using osu.Framework.Input.Bindings;
using osu.Game.Beatmaps.ControlPoints;
-using osu.Framework.Audio;
-using osu.Framework.Audio.Sample;
-using System.Collections.Generic;
-using osu.Game.Audio;
-using System;
namespace osu.Game.Rulesets.Taiko.UI
{
- public class TaikoPlayfield : ScrollingPlayfield, IKeyBindingHandler
+ public class TaikoPlayfield : ScrollingPlayfield
{
///
/// Default height of a when inside a .
@@ -61,13 +55,9 @@ namespace osu.Game.Rulesets.Taiko.UI
private readonly Box overlayBackground;
private readonly Box background;
- private readonly ControlPointInfo controlPointInfo;
- private Dictionary drumSampleMappings;
-
- public TaikoPlayfield(ControlPointInfo controlPointInfo)
+ public TaikoPlayfield(ControlPointInfo controlPoints)
: base(Axes.X)
{
- this.controlPointInfo = controlPointInfo;
AddRangeInternal(new Drawable[]
{
backgroundContainer = new Container
@@ -160,7 +150,7 @@ namespace osu.Game.Rulesets.Taiko.UI
{
RelativeSizeAxes = Axes.Both,
},
- new InputDrum
+ new InputDrum(controlPoints)
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
@@ -205,19 +195,8 @@ namespace osu.Game.Rulesets.Taiko.UI
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours, AudioManager audio)
+ private void load(OsuColour colours)
{
- drumSampleMappings = new Dictionary();
- foreach (var s in controlPointInfo.SamplePoints)
- {
- drumSampleMappings.Add(s,
- new DrumSamples
- {
- Centre = s.GetSampleInfo().GetChannel(audio.Sample),
- Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample)
- });
- }
-
overlayBackgroundContainer.BorderColour = colours.Gray0;
overlayBackground.Colour = colours.Gray1;
@@ -282,28 +261,5 @@ namespace osu.Game.Rulesets.Taiko.UI
kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim));
}
}
-
- public bool OnPressed(TaikoAction action)
- {
- var samplePoint = controlPointInfo.SamplePointAt(Clock.CurrentTime);
-
- if (!drumSampleMappings.TryGetValue(samplePoint, out var samples))
- throw new InvalidOperationException("Current sample set not found.");
-
- if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre)
- samples.Centre.Play();
- else
- samples.Rim.Play();
-
- return true;
- }
-
- public bool OnReleased(TaikoAction action) => false;
-
- private class DrumSamples
- {
- public SampleChannel Centre;
- public SampleChannel Rim;
- }
}
}
diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
index bb02db62b9..1aed86f8f9 100644
--- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
+++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
@@ -36,14 +36,15 @@
$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll
True
-
- $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
+
+ $(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll
True
+
@@ -82,6 +83,7 @@
+
diff --git a/osu.Game.Rulesets.Taiko/packages.config b/osu.Game.Rulesets.Taiko/packages.config
index cde428acea..b39a85a382 100644
--- a/osu.Game.Rulesets.Taiko/packages.config
+++ b/osu.Game.Rulesets.Taiko/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
index 639befef74..c09b987407 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
@@ -70,6 +70,7 @@ namespace osu.Game.Tests.Visual
testRemoveAll();
testEmptyTraversal();
+ testHiding();
}
private void ensureRandomFetchSuccess() =>
@@ -295,6 +296,40 @@ namespace osu.Game.Tests.Visual
checkNoSelection();
}
+ private void testHiding()
+ {
+ var hidingSet = createTestBeatmapSet(1);
+ hidingSet.Beatmaps[1].Hidden = true;
+ AddStep("Add set with diff 2 hidden", () => carousel.UpdateBeatmapSet(hidingSet));
+ setSelected(1, 1);
+
+ checkVisibleItemCount(true, 2);
+ advanceSelection(true);
+ checkSelected(1, 3);
+
+ setHidden(3);
+ checkSelected(1, 1);
+
+ setHidden(2, false);
+ advanceSelection(true);
+ checkSelected(1, 2);
+
+ setHidden(1);
+ checkSelected(1, 2);
+
+ setHidden(2);
+ checkNoSelection();
+
+ void setHidden(int diff, bool hidden = true)
+ {
+ AddStep((hidden ? "" : "un") + $"hide diff {diff}", () =>
+ {
+ hidingSet.Beatmaps[diff - 1].Hidden = hidden;
+ carousel.UpdateBeatmapSet(hidingSet);
+ });
+ }
+ }
+
private BeatmapSetInfo createTestBeatmapSet(int i)
{
return new BeatmapSetInfo
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
index 0168cedc86..3a50e43239 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
@@ -1,69 +1,161 @@
// 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 OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Select;
+using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual
{
public class TestCaseBeatmapInfoWedge : OsuTestCase
{
- private BeatmapManager beatmaps;
- private readonly Random random;
- private readonly BeatmapInfoWedge infoWedge;
+ private RulesetStore rulesets;
+ private TestBeatmapInfoWedge infoWedge;
+ private readonly List beatmaps = new List();
private readonly Bindable beatmap = new Bindable();
- public TestCaseBeatmapInfoWedge()
+ [BackgroundDependencyLoader]
+ private void load(OsuGameBase game, RulesetStore rulesets)
{
- random = new Random(0123);
+ this.rulesets = rulesets;
- Add(infoWedge = new BeatmapInfoWedge
+ beatmap.BindTo(game.Beatmap);
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ Add(infoWedge = new TestBeatmapInfoWedge
{
Size = new Vector2(0.5f, 245),
RelativeSizeAxes = Axes.X,
- Margin = new MarginPadding
- {
- Top = 20,
- },
+ Margin = new MarginPadding { Top = 20 }
});
AddStep("show", () =>
{
- Content.FadeInFromZero(250);
infoWedge.State = Visibility.Visible;
infoWedge.UpdateBeatmap(beatmap);
});
- AddStep("hide", () =>
+
+ AddWaitStep(3);
+
+ AddStep("hide", () => { infoWedge.State = Visibility.Hidden; });
+
+ AddWaitStep(3);
+
+ AddStep("show", () => { infoWedge.State = Visibility.Visible; });
+
+ foreach (var rulesetInfo in rulesets.AvailableRulesets)
{
- infoWedge.State = Visibility.Hidden;
- Content.FadeOut(100);
+ var ruleset = rulesetInfo.CreateInstance();
+ beatmaps.Add(createTestBeatmap(rulesetInfo));
+
+ var name = rulesetInfo.ShortName;
+ selectBeatmap(name);
+
+ // TODO: adjust cases once more info is shown for other gamemodes
+ switch (ruleset)
+ {
+ case OsuRuleset osu:
+ testOsuBeatmap(osu);
+ testInfoLabels(5);
+ break;
+ default:
+ testInfoLabels(2);
+ break;
+ }
+ }
+
+ testNullBeatmap();
+ }
+
+ private void testOsuBeatmap(OsuRuleset ruleset)
+ {
+ AddAssert("check version", () => infoWedge.Info.VersionLabel.Text == $"{ruleset.ShortName}Version");
+ AddAssert("check title", () => infoWedge.Info.TitleLabel.Text == $"{ruleset.ShortName}Source — {ruleset.ShortName}Title");
+ AddAssert("check artist", () => infoWedge.Info.ArtistLabel.Text == $"{ruleset.ShortName}Artist");
+ AddAssert("check author", () => infoWedge.Info.MapperContainer.Children.OfType().Any(s => s.Text == $"{ruleset.ShortName}Author"));
+ }
+
+ private void testInfoLabels(int expectedCount)
+ {
+ AddAssert("check infolabels exists", () => infoWedge.Info.InfoLabelContainer.Children.Any());
+ AddAssert("check infolabels count", () => infoWedge.Info.InfoLabelContainer.Children.Count == expectedCount);
+ }
+
+ private void testNullBeatmap()
+ {
+ selectNullBeatmap();
+ AddAssert("check empty version", () => string.IsNullOrEmpty(infoWedge.Info.VersionLabel.Text));
+ AddAssert("check default title", () => infoWedge.Info.TitleLabel.Text == beatmap.Default.BeatmapInfo.Metadata.Title);
+ AddAssert("check default artist", () => infoWedge.Info.ArtistLabel.Text == beatmap.Default.BeatmapInfo.Metadata.Artist);
+ AddAssert("check empty author", () => !infoWedge.Info.MapperContainer.Children.Any());
+ AddAssert("check no infolabels", () => !infoWedge.Info.InfoLabelContainer.Children.Any());
+ }
+
+ private void selectBeatmap(string name)
+ {
+ var infoBefore = infoWedge.Info;
+
+ AddStep($"select {name} beatmap", () =>
+ {
+ beatmap.Value = new TestWorkingBeatmap(beatmaps.First(b => b.BeatmapInfo.Ruleset.ShortName == name));
+ infoWedge.UpdateBeatmap(beatmap);
});
- AddStep("random beatmap", randomBeatmap);
- AddStep("null beatmap", () => infoWedge.UpdateBeatmap(beatmap.Default));
+
+ AddUntilStep(() => infoWedge.Info != infoBefore, "wait for async load");
}
- [BackgroundDependencyLoader]
- private void load(OsuGameBase game, BeatmapManager beatmaps)
+ private void selectNullBeatmap()
{
- this.beatmaps = beatmaps;
- beatmap.BindTo(game.Beatmap);
+ AddStep("select null beatmap", () =>
+ {
+ beatmap.Value = beatmap.Default;
+ infoWedge.UpdateBeatmap(beatmap);
+ });
}
- private void randomBeatmap()
+ private Beatmap createTestBeatmap(RulesetInfo ruleset)
{
- var sets = beatmaps.GetAllUsableBeatmapSets();
- if (sets.Count == 0)
- return;
+ List objects = new List();
+ for (double i = 0; i < 50000; i += 1000)
+ objects.Add(new HitObject { StartTime = i });
- var b = sets[random.Next(0, sets.Count)].Beatmaps[0];
- beatmap.Value = beatmaps.GetWorkingBeatmap(b);
- infoWedge.UpdateBeatmap(beatmap);
+ return new Beatmap
+ {
+ BeatmapInfo = new BeatmapInfo
+ {
+ Metadata = new BeatmapMetadata
+ {
+ AuthorString = $"{ruleset.ShortName}Author",
+ Artist = $"{ruleset.ShortName}Artist",
+ Source = $"{ruleset.ShortName}Source",
+ Title = $"{ruleset.ShortName}Title"
+ },
+ Ruleset = ruleset,
+ StarDifficulty = 6,
+ Version = $"{ruleset.ShortName}Version"
+ },
+ HitObjects = objects
+ };
+ }
+
+ private class TestBeatmapInfoWedge : BeatmapInfoWedge
+ {
+ public new BufferedWedgeInfo Info => base.Info;
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs
index cef8797f20..ad15833569 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs
@@ -160,9 +160,9 @@ namespace osu.Game.Tests.Visual
};
foreach(var s in scores)
{
- s.Statistics.Add("300", RNG.Next(2000));
- s.Statistics.Add("100", RNG.Next(2000));
- s.Statistics.Add("50", RNG.Next(2000));
+ s.Statistics.Add(HitResult.Great, RNG.Next(2000));
+ s.Statistics.Add(HitResult.Good, RNG.Next(2000));
+ s.Statistics.Add(HitResult.Meh, RNG.Next(2000));
}
anotherScores = new[]
@@ -272,9 +272,9 @@ namespace osu.Game.Tests.Visual
};
foreach (var s in anotherScores)
{
- s.Statistics.Add("300", RNG.Next(2000));
- s.Statistics.Add("100", RNG.Next(2000));
- s.Statistics.Add("50", RNG.Next(2000));
+ s.Statistics.Add(HitResult.Great, RNG.Next(2000));
+ s.Statistics.Add(HitResult.Good, RNG.Next(2000));
+ s.Statistics.Add(HitResult.Meh, RNG.Next(2000));
}
topScore = new OnlineScore
@@ -299,9 +299,9 @@ namespace osu.Game.Tests.Visual
TotalScore = 987654321,
Accuracy = 0.8487,
};
- topScore.Statistics.Add("300", RNG.Next(2000));
- topScore.Statistics.Add("100", RNG.Next(2000));
- topScore.Statistics.Add("50", RNG.Next(2000));
+ topScore.Statistics.Add(HitResult.Great, RNG.Next(2000));
+ topScore.Statistics.Add(HitResult.Good, RNG.Next(2000));
+ topScore.Statistics.Add(HitResult.Meh, RNG.Next(2000));
}
[BackgroundDependencyLoader]
diff --git a/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs
index bd5772d3bb..87552c3f17 100644
--- a/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs
@@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual
{
AddStep("Show overlay", () => failOverlay.Show());
- AddStep("Hover first button", () => failOverlay.Buttons.First().TriggerOnHover(null));
+ AddStep("Hover first button", () => failOverlay.Buttons.First().TriggerOnMouseMove(null));
AddStep("Hide overlay", () => failOverlay.Hide());
AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected));
@@ -162,7 +162,7 @@ namespace osu.Game.Tests.Visual
var secondButton = pauseOverlay.Buttons.Skip(1).First();
AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
- AddStep("Hover second button", () => secondButton.TriggerOnHover(null));
+ AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null));
AddAssert("First button not selected", () => !pauseOverlay.Buttons.First().Selected);
AddAssert("Second button selected", () => secondButton.Selected);
@@ -178,7 +178,7 @@ namespace osu.Game.Tests.Visual
var secondButton = pauseOverlay.Buttons.Skip(1).First();
- AddStep("Hover second button", () => secondButton.TriggerOnHover(null));
+ AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null));
AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
AddAssert("Second button not selected", () => !secondButton.Selected);
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected);
@@ -195,7 +195,7 @@ namespace osu.Game.Tests.Visual
var secondButton = pauseOverlay.Buttons.Skip(1).First();
- AddStep("Hover second button", () => secondButton.TriggerOnHover(null));
+ AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null));
AddStep("Unhover second button", () => secondButton.TriggerOnHoverLost(null));
AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); // Initial state condition
diff --git a/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs b/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs
index 79d3d7d4ba..46deca073f 100644
--- a/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs
@@ -1,21 +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.Collections.Generic;
using System.Linq;
-using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
namespace osu.Game.Tests.Visual
{
- [TestFixture]
public class TestCaseNotificationOverlay : OsuTestCase
{
private readonly NotificationOverlay manager;
+ private readonly List progressingNotifications = new List();
+
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(NotificationSection),
+ typeof(SimpleNotification),
+ typeof(ProgressNotification),
+ typeof(ProgressCompletionNotification),
+ typeof(IHasCompletionTarget),
+ typeof(Notification)
+ };
public TestCaseNotificationOverlay()
{
@@ -24,33 +35,65 @@ namespace osu.Game.Tests.Visual
Content.Add(manager = new NotificationOverlay
{
Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight,
+ Origin = Anchor.TopRight
});
- AddToggleStep(@"show", state => manager.State = state ? Visibility.Visible : Visibility.Hidden);
+ SpriteText displayedCount = new SpriteText();
- AddStep(@"simple #1", sendNotification1);
- AddStep(@"simple #2", sendNotification2);
- AddStep(@"progress #1", sendProgress1);
- AddStep(@"progress #2", sendProgress2);
- AddStep(@"barrage", () => sendBarrage());
+ Content.Add(displayedCount);
+
+ void setState(Visibility state) => AddStep(state.ToString(), () => manager.State = state);
+ void checkProgressingCount(int expected) => AddAssert($"progressing count is {expected}", () => progressingNotifications.Count == expected);
+
+ manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count}"; };
+
+
+ setState(Visibility.Visible);
+ AddStep(@"simple #1", sendHelloNotification);
+ AddStep(@"simple #2", sendAmazingNotification);
+ AddStep(@"progress #1", sendUploadProgress);
+ AddStep(@"progress #2", sendDownloadProgress);
+
+ checkProgressingCount(2);
+
+ setState(Visibility.Hidden);
+
+ AddRepeatStep(@"add many simple", sendManyNotifications, 3);
+ AddWaitStep(5);
+
+ checkProgressingCount(0);
+
+ AddStep(@"progress #3", sendUploadProgress);
+
+ checkProgressingCount(1);
+
+ AddAssert("Displayed count is 33", () => manager.UnreadCount.Value == 33);
+
+ AddWaitStep(10);
+
+ checkProgressingCount(0);
+
+
+ setState(Visibility.Visible);
+
+ //AddStep(@"barrage", () => sendBarrage());
}
- private void sendBarrage(int remaining = 100)
+ private void sendBarrage(int remaining = 10)
{
switch (RNG.Next(0, 4))
{
case 0:
- sendNotification1();
+ sendHelloNotification();
break;
case 1:
- sendNotification2();
+ sendAmazingNotification();
break;
case 2:
- sendProgress1();
+ sendUploadProgress();
break;
case 3:
- sendProgress2();
+ sendDownloadProgress();
break;
}
@@ -66,7 +109,7 @@ namespace osu.Game.Tests.Visual
if (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3)
{
- var p = progressingNotifications.FirstOrDefault(n => n.IsAlive && n.State == ProgressNotificationState.Queued);
+ var p = progressingNotifications.FirstOrDefault(n => n.State == ProgressNotificationState.Queued);
if (p != null)
p.State = ProgressNotificationState.Active;
}
@@ -74,13 +117,13 @@ namespace osu.Game.Tests.Visual
foreach (var n in progressingNotifications.FindAll(n => n.State == ProgressNotificationState.Active))
{
if (n.Progress < 1)
- n.Progress += (float)(Time.Elapsed / 2000) * RNG.NextSingle();
+ n.Progress += (float)(Time.Elapsed / 400) * RNG.NextSingle();
else
n.State = ProgressNotificationState.Completed;
}
}
- private void sendProgress2()
+ private void sendDownloadProgress()
{
var n = new ProgressNotification
{
@@ -91,9 +134,7 @@ namespace osu.Game.Tests.Visual
progressingNotifications.Add(n);
}
- private readonly List progressingNotifications = new List();
-
- private void sendProgress1()
+ private void sendUploadProgress()
{
var n = new ProgressNotification
{
@@ -104,14 +145,20 @@ namespace osu.Game.Tests.Visual
progressingNotifications.Add(n);
}
- private void sendNotification2()
+ private void sendAmazingNotification()
{
manager.Post(new SimpleNotification { Text = @"You are amazing" });
}
- private void sendNotification1()
+ private void sendHelloNotification()
{
manager.Post(new SimpleNotification { Text = @"Welcome to osu!. Enjoy your stay!" });
}
+
+ private void sendManyNotifications()
+ {
+ for (int i = 0; i < 10; i++)
+ manager.Post(new SimpleNotification { Text = @"Spam incoming!!" });
+ }
}
}
diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
index 6435df7c2c..18e40db064 100644
--- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
@@ -26,6 +26,7 @@ namespace osu.Game.Tests.Visual
private RulesetStore rulesets;
private DependencyContainer dependencies;
+ private WorkingBeatmap defaultBeatmap;
public override IReadOnlyList RequiredTypes => new[]
{
@@ -47,31 +48,61 @@ namespace osu.Game.Tests.Visual
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent);
+ private class TestSongSelect : PlaySongSelect
+ {
+ public WorkingBeatmap CurrentBeatmap => Beatmap.Value;
+ public new BeatmapCarousel Carousel => base.Carousel;
+ }
+
[BackgroundDependencyLoader]
private void load(BeatmapManager baseManager)
{
- PlaySongSelect songSelect;
+ TestSongSelect songSelect = null;
- if (manager == null)
+ var storage = new TestStorage(@"TestCasePlaySongSelect");
+
+ // this is by no means clean. should be replacing inside of OsuGameBase somehow.
+ var context = new OsuDbContext();
+
+ Func contextFactory = () => context;
+
+ dependencies.Cache(rulesets = new RulesetStore(contextFactory));
+ dependencies.Cache(manager = new BeatmapManager(storage, contextFactory, rulesets, null)
{
- var storage = new TestStorage(@"TestCasePlaySongSelect");
+ DefaultBeatmap = defaultBeatmap = baseManager.GetWorkingBeatmap(null)
+ });
- // this is by no means clean. should be replacing inside of OsuGameBase somehow.
- var context = new OsuDbContext();
+ void loadNewSongSelect(bool deleteMaps = false) => AddStep("reload song select", () =>
+ {
+ if (deleteMaps) manager.DeleteAll();
- Func contextFactory = () => context;
-
- dependencies.Cache(rulesets = new RulesetStore(contextFactory));
- dependencies.Cache(manager = new BeatmapManager(storage, contextFactory, rulesets, null)
+ if (songSelect != null)
{
- DefaultBeatmap = baseManager.GetWorkingBeatmap(null)
- });
+ Remove(songSelect);
+ songSelect.Dispose();
+ }
+ Add(songSelect = new TestSongSelect());
+ });
+
+ loadNewSongSelect(true);
+
+ AddWaitStep(3);
+
+ AddAssert("dummy selected", () => songSelect.CurrentBeatmap == defaultBeatmap);
+
+ AddStep("import test maps", () =>
+ {
for (int i = 0; i < 100; i += 10)
manager.Import(createTestBeatmapSet(i));
- }
+ });
- Add(songSelect = new PlaySongSelect());
+ AddWaitStep(3);
+ AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap);
+
+ loadNewSongSelect();
+ AddWaitStep(3);
+ AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap);
AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; });
AddStep(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; });
diff --git a/osu.Game.Tests/Visual/TestCasePopupDialog.cs b/osu.Game.Tests/Visual/TestCasePopupDialog.cs
new file mode 100644
index 0000000000..ed9c47a253
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCasePopupDialog.cs
@@ -0,0 +1,37 @@
+// 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;
+using osu.Game.Overlays.Dialog;
+
+namespace osu.Game.Tests.Visual
+{
+ public class TestCasePopupDialog : OsuTestCase
+ {
+ public TestCasePopupDialog()
+ {
+ var popup = new PopupDialog
+ {
+ RelativeSizeAxes = Axes.Both,
+ State = Framework.Graphics.Containers.Visibility.Visible,
+ Icon = FontAwesome.fa_assistive_listening_systems,
+ HeaderText = @"This is a test popup",
+ BodyText = "I can say lots of stuff and even wrap my words!",
+ Buttons = new PopupDialogButton[]
+ {
+ new PopupDialogCancelButton
+ {
+ Text = @"Yes. That you can.",
+ },
+ new PopupDialogOkButton
+ {
+ Text = @"You're a fake!",
+ },
+ }
+ };
+
+ Add(popup);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseResults.cs b/osu.Game.Tests/Visual/TestCaseResults.cs
index d0c5aa4939..28ce9524e0 100644
--- a/osu.Game.Tests/Visual/TestCaseResults.cs
+++ b/osu.Game.Tests/Visual/TestCaseResults.cs
@@ -15,6 +15,15 @@ namespace osu.Game.Tests.Visual
{
private BeatmapManager beatmaps;
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(Score),
+ typeof(Results),
+ typeof(ResultsPage),
+ typeof(ResultsPageScore),
+ typeof(ResultsPageRanking)
+ };
+
[BackgroundDependencyLoader]
private void load(BeatmapManager beatmaps)
{
@@ -41,12 +50,12 @@ namespace osu.Game.Tests.Visual
MaxCombo = 123,
Rank = ScoreRank.A,
Date = DateTimeOffset.Now,
- Statistics = new Dictionary
+ Statistics = new Dictionary
{
- { "300", 50 },
- { "100", 20 },
- { "50", 50 },
- { "x", 1 }
+ { HitResult.Great, 50 },
+ { HitResult.Good, 20 },
+ { HitResult.Meh, 50 },
+ { HitResult.Miss, 1 }
},
User = new User
{
diff --git a/osu.Game.Tests/Visual/TestCaseToolbar.cs b/osu.Game.Tests/Visual/TestCaseToolbar.cs
new file mode 100644
index 0000000000..9f538af09b
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseToolbar.cs
@@ -0,0 +1,39 @@
+// 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.Graphics.Containers;
+using osu.Game.Overlays.Toolbar;
+
+namespace osu.Game.Tests.Visual
+{
+ public class TestCaseToolbar : OsuTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(ToolbarButton),
+ typeof(ToolbarModeSelector),
+ typeof(ToolbarModeButton),
+ typeof(ToolbarNotificationButton),
+ };
+
+ public TestCaseToolbar()
+ {
+ var toolbar = new Toolbar { State = Visibility.Visible };
+
+ Add(toolbar);
+
+ var notificationButton = toolbar.Children.OfType().Last().Children.OfType().First();
+
+ void setNotifications(int count) => AddStep($"set notification count to {count}", () => notificationButton.NotificationCount.Value = count);
+
+ setNotifications(1);
+ setNotifications(2);
+ setNotifications(3);
+ setNotifications(0);
+ setNotifications(144);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs
index 38d59f03b5..13b6509740 100644
--- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs
+++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs
@@ -13,6 +13,8 @@ namespace osu.Game.Tests.Visual
{
public class TestCaseUserProfile : OsuTestCase
{
+ private readonly TestUserProfileOverlay profile;
+
public override IReadOnlyList RequiredTypes => new[]
{
typeof(ProfileHeader),
@@ -23,8 +25,12 @@ namespace osu.Game.Tests.Visual
public TestCaseUserProfile()
{
- var profile = new UserProfileOverlay();
- Add(profile);
+ Add(profile = new TestUserProfileOverlay());
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
AddStep("Show offline dummy", () => profile.ShowUser(new User
{
@@ -48,6 +54,9 @@ namespace osu.Game.Tests.Visual
Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray()
}
}, false));
+
+ checkSupporterTag(false);
+
AddStep("Show ppy", () => profile.ShowUser(new User
{
Username = @"peppy",
@@ -55,6 +64,9 @@ namespace osu.Game.Tests.Visual
Country = new Country { FullName = @"Australia", FlagName = @"AU" },
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg"
}));
+
+ checkSupporterTag(true);
+
AddStep("Show flyte", () => profile.ShowUser(new User
{
Username = @"flyte",
@@ -62,8 +74,23 @@ namespace osu.Game.Tests.Visual
Country = new Country { FullName = @"Japan", FlagName = @"JP" },
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
}));
+
AddStep("Hide", profile.Hide);
AddStep("Show without reload", profile.Show);
}
+
+ private void checkSupporterTag(bool isSupporter)
+ {
+ AddUntilStep(() => profile.Header.User != null, "wait for load");
+ if (isSupporter)
+ AddAssert("is supporter", () => profile.Header.SupporterTag.Alpha == 1);
+ else
+ AddAssert("no supporter", () => profile.Header.SupporterTag.Alpha == 0);
+ }
+
+ private class TestUserProfileOverlay : UserProfileOverlay
+ {
+ public new ProfileHeader Header => base.Header;
+ }
}
}
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index ff012bb6e2..8c04874e75 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -37,8 +37,8 @@
$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll
True
-
- $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
+
+ $(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll
True
@@ -134,6 +134,7 @@
+
@@ -148,6 +149,7 @@
+
diff --git a/osu.Game.Tests/packages.config b/osu.Game.Tests/packages.config
index e09f2a07ba..9fbb0537bc 100644
--- a/osu.Game.Tests/packages.config
+++ b/osu.Game.Tests/packages.config
@@ -6,6 +6,6 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
-
+
\ No newline at end of file
diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs
index 64a9aa50a0..71975bf0fa 100644
--- a/osu.Game/Audio/SampleInfo.cs
+++ b/osu.Game/Audio/SampleInfo.cs
@@ -14,10 +14,20 @@ namespace osu.Game.Audio
public const string HIT_NORMAL = @"hitnormal";
public const string HIT_CLAP = @"hitclap";
- public SampleChannel GetChannel(SampleManager manager)
+ public SampleChannel GetChannel(SampleManager manager, string resourceNamespace = null)
{
- var channel = manager.Get($"Gameplay/{Bank}-{Name}");
- channel.Volume.Value = Volume / 100.0;
+ SampleChannel channel = null;
+
+ if (resourceNamespace != null)
+ channel = manager.Get($"Gameplay/{resourceNamespace}/{Bank}-{Name}");
+
+ // try without namespace as a fallback.
+ if (channel == null)
+ channel = manager.Get($"Gameplay/{Bank}-{Name}");
+
+ if (channel != null)
+ channel.Volume.Value = Volume / 100.0;
+
return channel;
}
diff --git a/osu.Game/Audio/SampleInfoList.cs b/osu.Game/Audio/SampleInfoList.cs
deleted file mode 100644
index 06dd716a4a..0000000000
--- a/osu.Game/Audio/SampleInfoList.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System.Collections.Generic;
-
-namespace osu.Game.Audio
-{
- public class SampleInfoList : List
- {
- public SampleInfoList()
- {
- }
-
- public SampleInfoList(IEnumerable elements) : base(elements)
- {
- }
- }
-}
\ No newline at end of file
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 0325785016..c86860f7b0 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -697,10 +697,12 @@ namespace osu.Game.Beatmaps
}
}
+ public bool StableInstallationAvailable => GetStableStorage?.Invoke() != null;
+
///
/// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future.
///
- public void ImportFromStable()
+ public async Task ImportFromStable()
{
var stable = GetStableStorage?.Invoke();
@@ -710,7 +712,7 @@ namespace osu.Game.Beatmaps
return;
}
- Import(stable.GetDirectories("Songs"));
+ await Task.Factory.StartNew(() => Import(stable.GetDirectories("Songs")), TaskCreationOptions.LongRunning);
}
public void DeleteAll()
diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
index 40e45da13c..c2c13e1909 100644
--- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
@@ -17,7 +17,7 @@ namespace osu.Game.Beatmaps.ControlPoints
///
/// The default sample volume at this control point.
///
- public int SampleVolume;
+ public int SampleVolume = 100;
///
/// Create a SampleInfo based on the sample settings in this control point.
diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
index 1434943da0..3ec83ed8d5 100644
--- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
@@ -21,8 +21,7 @@ namespace osu.Game.Beatmaps
Metadata = new BeatmapMetadata
{
Artist = "please load a beatmap!",
- Title = "no beatmaps available!",
- AuthorString = "no one",
+ Title = "no beatmaps available!"
},
BeatmapSet = new BeatmapSetInfo(),
BaseDifficulty = new BeatmapDifficulty
diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs
index 065c770738..14605081b6 100644
--- a/osu.Game/Online/API/Requests/GetScoresRequest.cs
+++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs
@@ -122,26 +122,26 @@ namespace osu.Game.Online.API.Requests
{
foreach (var kvp in value)
{
- string key = kvp.Key;
- switch (key)
+ HitResult newKey;
+ switch (kvp.Key)
{
case @"count_300":
- key = @"300";
+ newKey = HitResult.Great;
break;
case @"count_100":
- key = @"100";
+ newKey = HitResult.Good;
break;
case @"count_50":
- key = @"50";
+ newKey = HitResult.Meh;
break;
case @"count_miss":
- key = @"x";
+ newKey = HitResult.Miss;
break;
default:
continue;
}
- Statistics.Add(key, kvp.Value);
+ Statistics.Add(newKey, kvp.Value);
}
}
}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 4745733bd9..2bc32794d7 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -16,6 +16,7 @@ using osu.Game.Screens;
using osu.Game.Screens.Menu;
using OpenTK;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Input.Bindings;
using osu.Framework.Platform;
@@ -37,7 +38,7 @@ namespace osu.Game
private MusicController musicController;
- private NotificationOverlay notificationOverlay;
+ private NotificationOverlay notifications;
private DialogOverlay dialogOverlay;
@@ -64,6 +65,8 @@ namespace osu.Game
public float ToolbarOffset => Toolbar.Position.Y + Toolbar.DrawHeight;
+ public readonly BindableBool ShowOverlays = new BindableBool();
+
private OsuScreen screenStack;
private VolumeControl volume;
@@ -136,7 +139,7 @@ namespace osu.Game
if (s.Beatmap == null)
{
- notificationOverlay.Post(new SimpleNotification
+ notifications.Post(new SimpleNotification
{
Text = @"Tried to load a score for a beatmap we don't have!",
Icon = FontAwesome.fa_life_saver,
@@ -154,7 +157,7 @@ namespace osu.Game
base.LoadComplete();
// hook up notifications to components.
- BeatmapManager.PostNotification = n => notificationOverlay?.Post(n);
+ BeatmapManager.PostNotification = n => notifications?.Post(n);
BeatmapManager.GetStableStorage = GetStorageForStableInstall;
AddRange(new Drawable[]
@@ -207,8 +210,9 @@ namespace osu.Game
Origin = Anchor.TopRight,
}, overlayContent.Add);
- loadComponentSingleFile(notificationOverlay = new NotificationOverlay
+ loadComponentSingleFile(notifications = new NotificationOverlay
{
+ GetToolbarHeight = () => ToolbarOffset,
Depth = -4,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
@@ -219,15 +223,7 @@ namespace osu.Game
Depth = -6,
}, overlayContent.Add);
- Logger.NewEntry += entry =>
- {
- if (entry.Level < LogLevel.Important) return;
-
- notificationOverlay.Post(new SimpleNotification
- {
- Text = $@"{entry.Level}: {entry.Message}"
- });
- };
+ forwardLoggedErrorsToNotifications();
dependencies.Cache(settings);
dependencies.Cache(social);
@@ -236,7 +232,7 @@ namespace osu.Game
dependencies.Cache(userProfile);
dependencies.Cache(musicController);
dependencies.Cache(beatmapSetOverlay);
- dependencies.Cache(notificationOverlay);
+ dependencies.Cache(notifications);
dependencies.Cache(dialogOverlay);
// ensure only one of these overlays are open at once.
@@ -271,22 +267,69 @@ namespace osu.Game
};
}
- settings.StateChanged += delegate
+ void updateScreenOffset()
{
- switch (settings.State)
+ float offset = 0;
+
+ if (settings.State == Visibility.Visible)
+ offset += ToolbarButton.WIDTH / 2;
+ if (notifications.State == Visibility.Visible)
+ offset -= ToolbarButton.WIDTH / 2;
+
+ screenStack.MoveToX(offset, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint);
+ }
+
+ settings.StateChanged += _ => updateScreenOffset();
+ notifications.StateChanged += _ => updateScreenOffset();
+
+ notifications.Enabled.BindTo(ShowOverlays);
+
+ ShowOverlays.ValueChanged += visible =>
+ {
+ //central game screen change logic.
+ if (!visible)
{
- case Visibility.Hidden:
- intro.MoveToX(0, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint);
- break;
- case Visibility.Visible:
- intro.MoveToX(SettingsOverlay.SIDEBAR_WIDTH / 2, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint);
- break;
+ hideAllOverlays();
+ musicController.State = Visibility.Hidden;
+ Toolbar.State = Visibility.Hidden;
}
+ else
+ Toolbar.State = Visibility.Visible;
};
Cursor.State = Visibility.Hidden;
}
+ private void forwardLoggedErrorsToNotifications()
+ {
+ int recentErrorCount = 0;
+
+ const double debounce = 5000;
+
+ Logger.NewEntry += entry =>
+ {
+ if (entry.Level < LogLevel.Error || entry.Target == null) return;
+
+ if (recentErrorCount < 2)
+ {
+ notifications.Post(new SimpleNotification
+ {
+ Icon = FontAwesome.fa_bomb,
+ Text = (recentErrorCount == 0 ? entry.Message : "Subsequent errors occurred and have been logged.") + "\nClick to view log files.",
+ Activated = () =>
+ {
+ Host.Storage.GetStorageForDirectory("logs").OpenInNativeExplorer();
+ return true;
+ }
+ });
+ }
+
+ Interlocked.Increment(ref recentErrorCount);
+
+ Scheduler.AddDelayed(() => Interlocked.Decrement(ref recentErrorCount), debounce);
+ };
+ }
+
private Task asyncLoadStream;
private void loadComponentSingleFile(T d, Action add)
@@ -335,8 +378,6 @@ namespace osu.Game
public bool OnReleased(GlobalAction action) => false;
- public event Action ScreenChanged;
-
private Container mainContent;
private Container overlayContent;
@@ -351,30 +392,7 @@ namespace osu.Game
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;
-
- if (currentScreen == null)
- {
- Exit();
- return;
- }
-
- //central game screen change logic.
- if (!currentScreen.ShowOverlays)
- {
- hideAllOverlays();
- musicController.State = Visibility.Hidden;
- Toolbar.State = Visibility.Hidden;
- }
- else
- Toolbar.State = Visibility.Visible;
-
- ScreenChanged?.Invoke(newScreen);
+ notifications.State = Visibility.Hidden;
}
protected override bool OnExiting()
@@ -422,15 +440,18 @@ namespace osu.Game
private void screenAdded(Screen newScreen)
{
+ currentScreen = (OsuScreen)newScreen;
+
newScreen.ModePushed += screenAdded;
newScreen.Exited += screenRemoved;
-
- screenChanged(newScreen);
}
private void screenRemoved(Screen newScreen)
{
- screenChanged(newScreen);
+ currentScreen = (OsuScreen)newScreen;
+
+ if (newScreen == null)
+ Exit();
}
}
}
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 0ddff5e5aa..ea0bf22112 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -177,8 +177,7 @@ namespace osu.Game
}
catch (MigrationFailedException e)
{
- Logger.Log((e.InnerException ?? e).ToString(), LoggingTarget.Database, LogLevel.Error);
- Logger.Log("Migration failed! We'll be starting with a fresh database.", LoggingTarget.Database, LogLevel.Error);
+ Logger.Error(e.InnerException ?? e, "Migration failed! We'll be starting with a fresh database.", LoggingTarget.Database);
// if we failed, let's delete the database and start fresh.
// todo: we probably want a better (non-destructive) migrations/recovery process at a later point than this.
diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs
index bd108a193b..b4aea898b2 100644
--- a/osu.Game/Overlays/BeatmapSet/Info.cs
+++ b/osu.Game/Overlays/BeatmapSet/Info.cs
@@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
@@ -176,7 +177,7 @@ namespace osu.Game.Overlays.BeatmapSet
Shadow = false,
Margin = new MarginPadding { Top = 20 },
},
- textFlow = new TextFlowContainer
+ textFlow = new OsuTextFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs
index 5a3aba7b43..2d5913d8ca 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs
@@ -12,6 +12,7 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
@@ -48,7 +49,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Font = @"Exo2.0-RegularItalic",
Margin = new MarginPadding { Left = side_margin }
},
- new DrawableFlag(score.User.Country?.FlagName)
+ new DrawableFlag(score.User.Country)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
@@ -104,7 +105,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
- Text = $"{score.Statistics["300"]}/{score.Statistics["100"]}/{score.Statistics["50"]}",
+ Text = $"{score.Statistics[HitResult.Great]}/{score.Statistics[HitResult.Good]}/{score.Statistics[HitResult.Meh]}",
Font = @"Exo2.0-RegularItalic",
Margin = new MarginPadding { Right = side_margin }
},
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs
index 833ed94c0f..e3b878587d 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs
@@ -52,13 +52,13 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
score = value;
avatar.User = username.User = score.User;
- flag.FlagName = score.User.Country?.FlagName;
+ flag.Country = score.User.Country;
date.Text = $@"achieved {score.Date:MMM d, yyyy}";
rank.UpdateRank(score.Rank);
totalScore.Value = $@"{score.TotalScore:N0}";
accuracy.Value = $@"{score.Accuracy:P2}";
- statistics.Value = $"{score.Statistics["300"]}/{score.Statistics["100"]}/{score.Statistics["50"]}";
+ statistics.Value = $"{score.Statistics[HitResult.Great]}/{score.Statistics[HitResult.Good]}/{score.Statistics[HitResult.Meh]}";
modsContainer.Clear();
foreach (Mod mod in score.Mods)
diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs
index 9b19b8150e..d2bd50cad6 100644
--- a/osu.Game/Overlays/Dialog/PopupDialog.cs
+++ b/osu.Game/Overlays/Dialog/PopupDialog.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Dialog
private readonly FillFlowContainer buttonsContainer;
private readonly SpriteIcon icon;
private readonly SpriteText header;
- private readonly SpriteText body;
+ private readonly TextFlowContainer body;
public FontAwesome Icon
{
@@ -48,7 +48,6 @@ namespace osu.Game.Overlays.Dialog
public string BodyText
{
- get { return body.Text; }
set { body.Text = value; }
}
@@ -220,17 +219,15 @@ namespace osu.Game.Overlays.Dialog
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
- Text = @"Header",
TextSize = 25,
Shadow = true,
},
- body = new OsuSpriteText
+ body = new OsuTextFlowContainer(t => t.TextSize = 18)
{
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Text = @"Body",
- TextSize = 18,
- Shadow = true,
+ Padding = new MarginPadding(15),
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ TextAnchor = Anchor.TopCentre,
},
},
},
diff --git a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs
index 53d77dab6c..c6be428987 100644
--- a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs
+++ b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs
@@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
@@ -88,7 +89,7 @@ namespace osu.Game.Overlays.MedalSplash
Alpha = 0f,
Scale = new Vector2(1f / scale_when_full),
},
- description = new TextFlowContainer
+ description = new OsuTextFlowContainer
{
TextAnchor = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs
index 77b7c3add2..35c2e9234d 100644
--- a/osu.Game/Overlays/Mods/ModButton.cs
+++ b/osu.Game/Overlays/Mods/ModButton.cs
@@ -204,13 +204,13 @@ namespace osu.Game.Overlays.Mods
{
iconsContainer.AddRange(new[]
{
- backgroundIcon = new ModIcon(Mods[1])
+ backgroundIcon = new PassThroughTooltipModIcon(Mods[1])
{
Origin = Anchor.BottomRight,
Anchor = Anchor.BottomRight,
Position = new Vector2(1.5f),
},
- foregroundIcon = new ModIcon(Mods[0])
+ foregroundIcon = new PassThroughTooltipModIcon(Mods[0])
{
Origin = Anchor.BottomRight,
Anchor = Anchor.BottomRight,
@@ -220,7 +220,7 @@ namespace osu.Game.Overlays.Mods
}
else
{
- iconsContainer.Add(foregroundIcon = new ModIcon(Mod)
+ iconsContainer.Add(foregroundIcon = new PassThroughTooltipModIcon(Mod)
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
@@ -259,5 +259,14 @@ namespace osu.Game.Overlays.Mods
Mod = mod;
}
+
+ private class PassThroughTooltipModIcon : ModIcon
+ {
+ public override string TooltipText => null;
+
+ public PassThroughTooltipModIcon(Mod mod) : base(mod)
+ {
+ }
+ }
}
}
diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs
index 260214a14f..5744e8c189 100644
--- a/osu.Game/Overlays/NotificationOverlay.cs
+++ b/osu.Game/Overlays/NotificationOverlay.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
-using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -10,6 +9,10 @@ using osu.Game.Overlays.Notifications;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
+using System;
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
+using osu.Framework.Threading;
namespace osu.Game.Overlays
{
@@ -19,9 +22,39 @@ namespace osu.Game.Overlays
public const float TRANSITION_LENGTH = 600;
- private ScrollContainer scrollContainer;
+ ///
+ /// Whether posted notifications should be processed.
+ ///
+ public readonly BindableBool Enabled = new BindableBool(true);
+
private FlowContainer sections;
+ ///
+ /// Provide a source for the toolbar height.
+ ///
+ public Func GetToolbarHeight;
+
+ public NotificationOverlay()
+ {
+ ScheduledDelegate notificationsEnabler = null;
+ Enabled.ValueChanged += v =>
+ {
+ if (!IsLoaded)
+ {
+ processingPosts = v;
+ return;
+ }
+
+ notificationsEnabler?.Cancel();
+
+ if (v)
+ // we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed.
+ notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, 1000);
+ else
+ processingPosts = false;
+ };
+ }
+
[BackgroundDependencyLoader]
private void load()
{
@@ -36,12 +69,12 @@ namespace osu.Game.Overlays
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
- Alpha = 0.6f,
+ Alpha = 0.6f
},
- scrollContainer = new OsuScrollContainer
+ new OsuScrollContainer
{
+ Masking = true,
RelativeSizeAxes = Axes.Both,
- Margin = new MarginPadding { Top = Toolbar.Toolbar.HEIGHT },
Children = new[]
{
sections = new FillFlowContainer
@@ -55,14 +88,14 @@ namespace osu.Game.Overlays
{
Title = @"Notifications",
ClearText = @"Clear All",
- AcceptTypes = new[] { typeof(SimpleNotification) },
+ AcceptTypes = new[] { typeof(SimpleNotification) }
},
new NotificationSection
{
Title = @"Running Tasks",
ClearText = @"Cancel All",
- AcceptTypes = new[] { typeof(ProgressNotification) },
- },
+ AcceptTypes = new[] { typeof(ProgressNotification) }
+ }
}
}
}
@@ -70,47 +103,59 @@ namespace osu.Game.Overlays
};
}
+ private int totalCount => sections.Select(c => c.DisplayedCount).Sum();
+ private int unreadCount => sections.Select(c => c.UnreadCount).Sum();
+
+ public readonly BindableInt UnreadCount = new BindableInt();
+
private int runningDepth;
private void notificationClosed()
- {
- // hide ourselves if all notifications have been dismissed.
- if (sections.Select(c => c.DisplayedCount).Sum() == 0)
- State = Visibility.Hidden;
- }
-
- public void Post(Notification notification)
{
Schedule(() =>
{
- State = Visibility.Visible;
-
- ++runningDepth;
- notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth;
-
- notification.Closed += notificationClosed;
-
- var hasCompletionTarget = notification as IHasCompletionTarget;
- if (hasCompletionTarget != null)
- hasCompletionTarget.CompletionTarget = Post;
-
- var ourType = notification.GetType();
- sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType)))?.Add(notification);
+ // hide ourselves if all notifications have been dismissed.
+ if (totalCount == 0)
+ State = Visibility.Hidden;
});
+
+ updateCounts();
+ }
+
+ private readonly Scheduler postScheduler = new Scheduler();
+
+ private bool processingPosts = true;
+
+ public void Post(Notification notification) => postScheduler.Add(() =>
+ {
+ ++runningDepth;
+ notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth;
+
+ notification.Closed += notificationClosed;
+
+ var hasCompletionTarget = notification as IHasCompletionTarget;
+ if (hasCompletionTarget != null)
+ hasCompletionTarget.CompletionTarget = Post;
+
+ var ourType = notification.GetType();
+ sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType)))?.Add(notification);
+
+ updateCounts();
+ });
+
+ protected override void Update()
+ {
+ base.Update();
+ if (processingPosts)
+ postScheduler.Update();
}
protected override void PopIn()
{
base.PopIn();
- scrollContainer.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint);
this.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint);
- this.FadeTo(1, TRANSITION_LENGTH / 2);
- }
-
- private void markAllRead()
- {
- sections.Children.ForEach(s => s.MarkAllRead());
+ this.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint);
}
protected override void PopOut()
@@ -120,7 +165,26 @@ namespace osu.Game.Overlays
markAllRead();
this.MoveToX(width, TRANSITION_LENGTH, Easing.OutQuint);
- this.FadeTo(0, TRANSITION_LENGTH / 2);
+ this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint);
+ }
+
+ private void updateCounts()
+ {
+ UnreadCount.Value = unreadCount;
+ }
+
+ private void markAllRead()
+ {
+ sections.Children.ForEach(s => s.MarkAllRead());
+
+ updateCounts();
+ }
+
+ protected override void UpdateAfterChildren()
+ {
+ base.UpdateAfterChildren();
+
+ Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 };
}
}
}
diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs
index 422051364e..dc2dcf2d74 100644
--- a/osu.Game/Overlays/Notifications/Notification.cs
+++ b/osu.Game/Overlays/Notifications/Notification.cs
@@ -91,7 +91,6 @@ namespace osu.Game.Overlays.Notifications
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding
{
- Top = 5,
Left = 45,
Right = 30
},
@@ -261,4 +260,4 @@ namespace osu.Game.Overlays.Notifications
}
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs
index 09768ba0ea..42fcc3aa0f 100644
--- a/osu.Game/Overlays/Notifications/NotificationSection.cs
+++ b/osu.Game/Overlays/Notifications/NotificationSection.cs
@@ -15,7 +15,7 @@ using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Notifications
{
- public class NotificationSection : FillFlowContainer
+ public class NotificationSection : AlwaysUpdateFillFlowContainer
{
private OsuSpriteText titleText;
private OsuSpriteText countText;
@@ -26,11 +26,14 @@ namespace osu.Game.Overlays.Notifications
public int DisplayedCount => notifications.Count(n => !n.WasClosed);
+ public int UnreadCount => notifications.Count(n => !n.WasClosed && !n.Read);
+
public void Add(Notification notification) => notifications.Add(notification);
public IEnumerable AcceptTypes;
private string clearText;
+
public string ClearText
{
get { return clearText; }
@@ -108,7 +111,7 @@ namespace osu.Game.Overlays.Notifications
},
},
},
- notifications = new FillFlowContainer
+ notifications = new AlwaysUpdateFillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
@@ -157,4 +160,13 @@ namespace osu.Game.Overlays.Notifications
notifications?.Children.ForEach(n => n.Read = true);
}
}
-}
\ No newline at end of file
+
+ public class AlwaysUpdateFillFlowContainer : FillFlowContainer
+ where T : Drawable
+ {
+ // this is required to ensure correct layout and scheduling on children.
+ // the layout portion of this is being tracked as a framework issue (https://github.com/ppy/osu-framework/issues/1297).
+ protected override bool RequiresChildrenUpdate => true;
+ }
+
+}
diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs
index 12c7fe64ba..d797372390 100644
--- a/osu.Game/Overlays/Notifications/ProgressNotification.cs
+++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs
@@ -7,6 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
using OpenTK;
using OpenTK.Graphics;
@@ -94,8 +95,8 @@ namespace osu.Game.Overlays.Notifications
protected virtual void Completed()
{
- Expire();
CompletionTarget?.Invoke(CreateCompletionNotification());
+ base.Close();
}
public override bool DisplayOnTop => false;
@@ -114,7 +115,7 @@ namespace osu.Game.Overlays.Notifications
RelativeSizeAxes = Axes.Both,
});
- Content.Add(textDrawable = new TextFlowContainer(t =>
+ Content.Add(textDrawable = new OsuTextFlowContainer(t =>
{
t.TextSize = 16;
})
diff --git a/osu.Game/Overlays/Notifications/SimpleNotification.cs b/osu.Game/Overlays/Notifications/SimpleNotification.cs
index daf1ac838d..1e691e2a5a 100644
--- a/osu.Game/Overlays/Notifications/SimpleNotification.cs
+++ b/osu.Game/Overlays/Notifications/SimpleNotification.cs
@@ -7,6 +7,7 @@ using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
using OpenTK;
namespace osu.Game.Overlays.Notifications
@@ -58,7 +59,7 @@ namespace osu.Game.Overlays.Notifications
}
});
- Content.Add(textDrawable = new TextFlowContainer(t => t.TextSize = 16)
+ Content.Add(textDrawable = new OsuTextFlowContainer(t => t.TextSize = 16)
{
Colour = OsuColour.Gray(128),
AutoSizeAxes = Axes.Y,
@@ -82,9 +83,11 @@ namespace osu.Game.Overlays.Notifications
set
{
+ if (value == base.Read) return;
+
base.Read = value;
- Light.FadeTo(value ? 1 : 0, 100);
+ Light.FadeTo(value ? 0 : 1, 100);
}
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs
index dcab942522..ce0feeb4c6 100644
--- a/osu.Game/Overlays/OnScreenDisplay.cs
+++ b/osu.Game/Overlays/OnScreenDisplay.cs
@@ -14,6 +14,7 @@ using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
+using osu.Game.Graphics.Sprites;
namespace osu.Game.Overlays
{
@@ -63,7 +64,7 @@ namespace osu.Game.Overlays
Width = 240,
RelativeSizeAxes = Axes.Y,
},
- textLine1 = new SpriteText
+ textLine1 = new OsuSpriteText
{
Padding = new MarginPadding(10),
Font = @"Exo2.0-Black",
@@ -72,7 +73,7 @@ namespace osu.Game.Overlays
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
- textLine2 = new SpriteText
+ textLine2 = new OsuSpriteText
{
TextSize = 24,
Font = @"Exo2.0-Light",
@@ -97,7 +98,7 @@ namespace osu.Game.Overlays
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Both
},
- textLine3 = new SpriteText
+ textLine3 = new OsuSpriteText
{
Padding = new MarginPadding { Bottom = 15 },
Font = @"Exo2.0-Bold",
diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs
index a706799664..960bb60287 100644
--- a/osu.Game/Overlays/Profile/ProfileHeader.cs
+++ b/osu.Game/Overlays/Profile/ProfileHeader.cs
@@ -29,7 +29,8 @@ namespace osu.Game.Overlays.Profile
private readonly FillFlowContainer scoreText, scoreNumberText;
private readonly RankGraph rankGraph;
- private readonly Container coverContainer, supporterTag;
+ public readonly SupporterIcon SupporterTag;
+ private readonly Container coverContainer;
private readonly Sprite levelBadge;
private readonly SpriteText levelText;
private readonly GradeBadge gradeSSPlus, gradeSS, gradeSPlus, gradeS, gradeA;
@@ -94,32 +95,13 @@ namespace osu.Game.Overlays.Profile
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
- supporterTag = new CircularContainer
+ SupporterTag = new SupporterIcon
{
+ Alpha = 0,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Y = -75,
- Size = new Vector2(25, 25),
- Masking = true,
- BorderThickness = 3,
- BorderColour = Color4.White,
- Alpha = 0,
- Children = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Alpha = 0,
- AlwaysPresent = true
- },
- new SpriteIcon
- {
- Icon = FontAwesome.fa_heart,
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Size = new Vector2(12),
- }
- }
+ Size = new Vector2(25, 25)
},
new LinkFlowContainer.ProfileLink(user)
{
@@ -127,7 +109,7 @@ namespace osu.Game.Overlays.Profile
Origin = Anchor.BottomLeft,
Y = -48,
},
- countryFlag = new DrawableFlag(user.Country?.FlagName)
+ countryFlag = new DrawableFlag(user.Country)
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
@@ -328,7 +310,8 @@ namespace osu.Game.Overlays.Profile
Depth = float.MaxValue,
}, coverContainer.Add);
- if (user.IsSupporter) supporterTag.Show();
+ if (user.IsSupporter)
+ SupporterTag.Show();
if (!string.IsNullOrEmpty(user.Colour))
{
@@ -350,7 +333,7 @@ namespace osu.Game.Overlays.Profile
{
infoTextLeft.AddText("from ");
infoTextLeft.AddText(user.Country.FullName, boldItalic);
- countryFlag.FlagName = user.Country.FlagName;
+ countryFlag.Country = user.Country;
}
infoTextLeft.NewParagraph();
@@ -473,7 +456,7 @@ namespace osu.Game.Overlays.Profile
Width = width,
Height = 26
});
- Add(numberText = new SpriteText
+ Add(numberText = new OsuSpriteText
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs
index 8a835634b8..904ed609e8 100644
--- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs
+++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs
@@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
@@ -120,7 +121,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
}
}
},
- new TextFlowContainer(t => { t.TextSize = 19; })
+ new OsuTextFlowContainer(t => { t.TextSize = 19; })
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
diff --git a/osu.Game/Overlays/Profile/SupporterIcon.cs b/osu.Game/Overlays/Profile/SupporterIcon.cs
new file mode 100644
index 0000000000..b5cd94e54b
--- /dev/null
+++ b/osu.Game/Overlays/Profile/SupporterIcon.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 osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Backgrounds;
+
+namespace osu.Game.Overlays.Profile
+{
+ public class SupporterIcon : CircularContainer, IHasTooltip
+ {
+ private readonly Box background;
+
+ public string TooltipText => "osu!supporter";
+
+ public SupporterIcon()
+ {
+ Masking = true;
+ Children = new Drawable[]
+ {
+ new Box { RelativeSizeAxes = Axes.Both },
+ new CircularContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Scale = new Vector2(0.8f),
+ Masking = true,
+ Children = new Drawable[]
+ {
+ background = new Box { RelativeSizeAxes = Axes.Both },
+ new Triangles
+ {
+ TriangleScale = 0.2f,
+ ColourLight = OsuColour.FromHex(@"ff7db7"),
+ ColourDark = OsuColour.FromHex(@"de5b95"),
+ RelativeSizeAxes = Axes.Both,
+ Velocity = 0.3f,
+ },
+ }
+ },
+ new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Icon = FontAwesome.fa_heart,
+ Scale = new Vector2(0.45f),
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ background.Colour = colours.Pink;
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs
index e288445c6d..9ab4143613 100644
--- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs
@@ -30,8 +30,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
Action = () =>
{
importButton.Enabled.Value = false;
- Task.Factory.StartNew(beatmaps.ImportFromStable)
- .ContinueWith(t => Schedule(() => importButton.Enabled.Value = true), TaskContinuationOptions.LongRunning);
+ beatmaps.ImportFromStable().ContinueWith(t => Schedule(() => importButton.Enabled.Value = true));
}
},
deleteButton = new DangerousSettingsButton
diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs
index 798fa00032..a80f6d4da8 100644
--- a/osu.Game/Overlays/SettingsOverlay.cs
+++ b/osu.Game/Overlays/SettingsOverlay.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Overlays
public const float TRANSITION_LENGTH = 600;
- public const float SIDEBAR_WIDTH = Sidebar.DEFAULT_WIDTH;
+ private const float sidebar_width = Sidebar.DEFAULT_WIDTH;
protected const float WIDTH = 400;
@@ -102,7 +102,7 @@ namespace osu.Game.Overlays
if (showSidebar)
{
- AddInternal(Sidebar = new Sidebar { Width = SIDEBAR_WIDTH });
+ AddInternal(Sidebar = new Sidebar { Width = sidebar_width });
SectionsContainer.SelectedSection.ValueChanged += section =>
{
@@ -167,7 +167,7 @@ namespace osu.Game.Overlays
ContentContainer.MoveToX(-WIDTH, TRANSITION_LENGTH, Easing.OutQuint);
- Sidebar?.MoveToX(-SIDEBAR_WIDTH, TRANSITION_LENGTH, Easing.OutQuint);
+ Sidebar?.MoveToX(-sidebar_width, TRANSITION_LENGTH, Easing.OutQuint);
this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint);
searchTextBox.HoldFocus = false;
diff --git a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs
index ed206e7e1d..b0171feb30 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Toolbar
SetIcon(FontAwesome.fa_comments);
}
- [BackgroundDependencyLoader]
+ [BackgroundDependencyLoader(true)]
private void load(ChatOverlay chat)
{
StateContainer = chat;
diff --git a/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs b/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs
index 7d25440e2c..5c64ae69ec 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Toolbar
SetIcon(FontAwesome.fa_osu_chevron_down_o);
}
- [BackgroundDependencyLoader]
+ [BackgroundDependencyLoader(true)]
private void load(DirectOverlay direct)
{
StateContainer = direct;
diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs
index 319dd63bc9..f38c772890 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs
@@ -64,7 +64,7 @@ namespace osu.Game.Overlays.Toolbar
};
}
- [BackgroundDependencyLoader]
+ [BackgroundDependencyLoader(true)]
private void load(RulesetStore rulesets, OsuGame game)
{
foreach (var r in rulesets.AvailableRulesets)
@@ -81,7 +81,10 @@ namespace osu.Game.Overlays.Toolbar
ruleset.ValueChanged += rulesetChanged;
ruleset.DisabledChanged += disabledChanged;
- ruleset.BindTo(game.Ruleset);
+ if (game != null)
+ ruleset.BindTo(game.Ruleset);
+ else
+ ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault();
}
public override bool HandleInput => !ruleset.Disabled;
diff --git a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs
index d150aacdf9..81c57a984f 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Toolbar
Icon = FontAwesome.fa_music;
}
- [BackgroundDependencyLoader]
+ [BackgroundDependencyLoader(true)]
private void load(MusicController music)
{
StateContainer = music;
diff --git a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs
index e11a22d675..c093767e52 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs
@@ -2,8 +2,14 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
+using osu.Framework.Configuration;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using OpenTK;
+using OpenTK.Graphics;
namespace osu.Game.Overlays.Toolbar
{
@@ -11,17 +17,96 @@ namespace osu.Game.Overlays.Toolbar
{
protected override Anchor TooltipAnchor => Anchor.TopRight;
+ public BindableInt NotificationCount = new BindableInt();
+
+ private readonly CountCircle countDisplay;
+
public ToolbarNotificationButton()
{
Icon = FontAwesome.fa_bars;
TooltipMain = "Notifications";
TooltipSub = "Waiting for 'ya";
+
+ Add(countDisplay = new CountCircle
+ {
+ Alpha = 0,
+ Height = 16,
+ RelativePositionAxes = Axes.Both,
+ Origin = Anchor.Centre,
+ Position = new Vector2(0.7f, 0.25f),
+ });
}
- [BackgroundDependencyLoader]
+ [BackgroundDependencyLoader(true)]
private void load(NotificationOverlay notificationOverlay)
{
StateContainer = notificationOverlay;
+
+ if (notificationOverlay != null)
+ NotificationCount.BindTo(notificationOverlay.UnreadCount);
+
+ NotificationCount.ValueChanged += count =>
+ {
+ if (count == 0)
+ countDisplay.FadeOut(200, Easing.OutQuint);
+ else
+ {
+ countDisplay.Count = count;
+ countDisplay.FadeIn(200, Easing.OutQuint);
+ }
+ };
+ }
+
+ private class CountCircle : CompositeDrawable
+ {
+ private readonly OsuSpriteText countText;
+ private readonly Circle circle;
+
+ private int count;
+
+ public int Count
+ {
+ get { return count; }
+ set
+ {
+ if (count == value)
+ return;
+
+ if (value > count)
+ {
+ circle.FlashColour(Color4.White, 600, Easing.OutQuint);
+ this.ScaleTo(1.1f).Then().ScaleTo(1, 600, Easing.OutElastic);
+ }
+
+ count = value;
+ countText.Text = value.ToString("#,0");
+ }
+ }
+
+ public CountCircle()
+ {
+ AutoSizeAxes = Axes.X;
+
+ InternalChildren = new Drawable[]
+ {
+ circle = new Circle
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Red
+ },
+ countText = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Y = -1,
+ TextSize = 14,
+ Padding = new MarginPadding(5),
+ Colour = Color4.White,
+ UseFullGlyphHeight = true,
+ Font = "Exo2.0-Bold",
+ }
+ };
+ }
}
}
}
diff --git a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs
index 69fdd27d5d..59314b8771 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs
@@ -21,8 +21,11 @@ namespace osu.Game.Overlays.Toolbar
set
{
stateContainer = value;
- Action = stateContainer.ToggleVisibility;
- stateContainer.StateChanged += stateChanged;
+ if (stateContainer != null)
+ {
+ Action = stateContainer.ToggleVisibility;
+ stateContainer.StateChanged += stateChanged;
+ }
}
}
diff --git a/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs
index cf4f664e81..d0d76dd5d2 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs
@@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Toolbar
TooltipSub = "Change your settings";
}
- [BackgroundDependencyLoader]
+ [BackgroundDependencyLoader(true)]
private void load(SettingsOverlay settings)
{
StateContainer = settings;
diff --git a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs
index 234d6f0f9a..74d1da4384 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Toolbar
Icon = FontAwesome.fa_users;
}
- [BackgroundDependencyLoader]
+ [BackgroundDependencyLoader(true)]
private void load(SocialOverlay chat)
{
StateContainer = chat;
diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs
index 7374a9aa44..9aa660147a 100644
--- a/osu.Game/Overlays/UserProfileOverlay.cs
+++ b/osu.Game/Overlays/UserProfileOverlay.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Overlays
private ProfileSection[] sections;
private GetUserRequest userReq;
private APIAccess api;
- private ProfileHeader header;
+ protected ProfileHeader Header;
private SectionsContainer sectionsContainer;
private ProfileTabControl tabs;
@@ -113,12 +113,12 @@ namespace osu.Game.Overlays
Colour = OsuColour.Gray(0.2f)
});
- header = new ProfileHeader(user);
+ Header = new ProfileHeader(user);
Add(sectionsContainer = new SectionsContainer
{
RelativeSizeAxes = Axes.Both,
- ExpandableHeader = header,
+ ExpandableHeader = Header,
FixedHeader = tabs,
HeaderBackground = new Box
{
@@ -169,7 +169,7 @@ namespace osu.Game.Overlays
private void userLoadComplete(User user)
{
- header.User = user;
+ Header.User = user;
foreach (string id in user.ProfileOrder)
{
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index 4487f74364..36740b96cb 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Edit
}
catch (Exception e)
{
- Logger.Log($"Could not load this beatmap sucessfully ({e})!", LoggingTarget.Runtime, LogLevel.Error);
+ Logger.Error(e, "Could not load beatmap sucessfully!");
return;
}
diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs
index 5ab4b7636b..3d7880f56f 100644
--- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs
+++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs
@@ -9,7 +9,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
-using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Judgements
{
diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs
index d804111a7f..f8c9b9734f 100644
--- a/osu.Game/Rulesets/Judgements/Judgement.cs
+++ b/osu.Game/Rulesets/Judgements/Judgement.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Judgements
{
diff --git a/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs b/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs
new file mode 100644
index 0000000000..1024d5686d
--- /dev/null
+++ b/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs
@@ -0,0 +1,20 @@
+// 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.Game.Rulesets.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Mods
+{
+ ///
+ /// An interface for s that can be applied to s.
+ ///
+ public interface IApplicableToDrawableHitObjects
+ {
+ ///
+ /// Applies this to a list of s.
+ ///
+ /// The list of s to apply to.
+ void ApplyToDrawableHitObjects(IEnumerable drawables);
+ }
+}
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index d960ab6b48..45a7275c53 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -16,6 +16,7 @@ using osu.Game.Graphics;
using osu.Framework.Configuration;
using OpenTK;
using osu.Framework.Graphics.Primitives;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Objects.Drawables
{
@@ -72,6 +73,10 @@ namespace osu.Game.Rulesets.Objects.Drawables
public IReadOnlyList Judgements => judgements;
protected List Samples = new List();
+ protected virtual IEnumerable GetSamples() => HitObject.Samples;
+
+ // Todo: Rulesets should be overriding the resources instead, but we need to figure out where/when to apply overrides first
+ protected virtual string SampleNamespace => null;
public readonly Bindable State = new Bindable();
@@ -84,12 +89,14 @@ namespace osu.Game.Rulesets.Objects.Drawables
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
- if (HitObject.Samples != null)
+ var samples = GetSamples();
+ if (samples.Any())
{
if (HitObject.SampleControlPoint == null)
- throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)} must always have an attached {nameof(HitObject.SampleControlPoint)}.");
+ throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}."
+ + $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}.");
- foreach (SampleInfo s in HitObject.Samples)
+ foreach (SampleInfo s in samples)
{
SampleInfo localSampleInfo = new SampleInfo
{
@@ -98,7 +105,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
Volume = s.Volume > 0 ? s.Volume : HitObject.SampleControlPoint.SampleVolume
};
- SampleChannel channel = localSampleInfo.GetChannel(audio.Sample);
+ SampleChannel channel = localSampleInfo.GetChannel(audio.Sample, SampleNamespace);
if (channel == null)
continue;
@@ -116,6 +123,9 @@ namespace osu.Game.Rulesets.Objects.Drawables
{
UpdateState(state);
+ // apply any custom state overrides
+ ApplyCustomUpdateState?.Invoke(this, state);
+
if (State == ArmedState.Hit)
PlaySamples();
};
@@ -174,7 +184,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
{
judgementOccurred = false;
- if (AllJudged || State != ArmedState.Idle)
+ if (AllJudged)
return false;
if (NestedHitObjects != null)
@@ -237,9 +247,15 @@ namespace osu.Game.Rulesets.Objects.Drawables
h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j);
h.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(d, j);
+ h.ApplyCustomUpdateState += (d, s) => ApplyCustomUpdateState?.Invoke(d, s);
nestedHitObjects.Add(h);
}
+ ///
+ /// Bind to apply a custom state which can override the default implementation.
+ ///
+ public event Action ApplyCustomUpdateState;
+
protected abstract void UpdateState(ArmedState state);
}
}
diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs
index e950516bf4..4f06f6afe1 100644
--- a/osu.Game/Rulesets/Objects/HitObject.cs
+++ b/osu.Game/Rulesets/Objects/HitObject.cs
@@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Objects
///
public virtual double StartTime { get; set; }
+ private List samples;
+
///
/// The samples to be played when this hit object is hit.
///
@@ -32,7 +34,11 @@ namespace osu.Game.Rulesets.Objects
/// and can be treated as the default samples for the hit object.
///
///
- public SampleInfoList Samples;
+ public List Samples
+ {
+ get => samples ?? (samples = new List());
+ set => samples = value;
+ }
[JsonIgnore]
public SampleControlPoint SampleControlPoint;
diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
index 667f921e04..fbf02f5345 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
};
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List repeatSamples)
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
{
return new ConvertSlider
{
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
index 0d7d617405..bdbd7a9e65 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
@@ -127,7 +127,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
}
// Generate the final per-node samples
- var nodeSamples = new List(nodes);
+ var nodeSamples = new List>(nodes);
for (int i = 0; i <= repeatCount; i++)
nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
@@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// The slider repeat count.
/// The samples to be played when the repeat nodes are hit. This includes the head and tail of the slider.
/// The hit object.
- protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List repeatSamples);
+ protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples);
///
/// Creates a legacy Spinner-type hit object.
@@ -234,9 +234,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// The hold end time.
protected abstract HitObject CreateHold(Vector2 position, bool newCombo, double endTime);
- private SampleInfoList convertSoundType(LegacySoundType type, SampleBankInfo bankInfo)
+ private List convertSoundType(LegacySoundType type, SampleBankInfo bankInfo)
{
- var soundTypes = new SampleInfoList
+ var soundTypes = new List
{
new SampleInfo
{
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs
index 698b74cc28..6dc8a07630 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
public double Distance { get; set; }
- public List RepeatSamples { get; set; }
+ public List> RepeatSamples { get; set; }
public int RepeatCount { get; set; } = 1;
public double EndTime => StartTime + RepeatCount * Distance / Velocity;
diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs
index 86dd40b06e..2060b84222 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
};
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List repeatSamples)
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
{
return new ConvertSlider
{
diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs
index 24c205db13..0062d29446 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs
@@ -2,9 +2,9 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
-using osu.Game.Audio;
using osu.Game.Rulesets.Objects.Types;
using System.Collections.Generic;
+using osu.Game.Audio;
namespace osu.Game.Rulesets.Objects.Legacy.Osu
{
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
};
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List repeatSamples)
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
{
return new ConvertSlider
{
diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs
index 0554cfd97d..529a28ac15 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs
@@ -2,9 +2,9 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
-using osu.Game.Audio;
using osu.Game.Rulesets.Objects.Types;
using System.Collections.Generic;
+using osu.Game.Audio;
namespace osu.Game.Rulesets.Objects.Legacy.Taiko
{
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
};
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List repeatSamples)
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
{
return new ConvertSlider
{
diff --git a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs
index 5abad2d661..2fe2424d49 100644
--- a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs
+++ b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs
@@ -19,6 +19,6 @@ namespace osu.Game.Rulesets.Objects.Types
///
/// The samples to be played when each repeat node is hit (0 -> first repeat node, 1 -> second repeat node, etc).
///
- List RepeatSamples { get; }
+ List> RepeatSamples { get; }
}
}
diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs
index 5cdf46ee46..3038c51e64 100644
--- a/osu.Game/Rulesets/RulesetStore.cs
+++ b/osu.Game/Rulesets/RulesetStore.cs
@@ -38,6 +38,13 @@ namespace osu.Game.Rulesets
/// A ruleset, if available, else null.
public RulesetInfo GetRuleset(int id) => AvailableRulesets.FirstOrDefault(r => r.ID == id);
+ ///
+ /// Retrieve a ruleset using a known short name.
+ ///
+ /// The ruleset's short name.
+ /// A ruleset, if available, else null.
+ public RulesetInfo GetRuleset(string shortName) => AvailableRulesets.FirstOrDefault(r => r.ShortName == shortName);
+
///
/// All available rulesets.
///
diff --git a/osu.Game/Rulesets/Objects/Drawables/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs
similarity index 91%
rename from osu.Game/Rulesets/Objects/Drawables/HitResult.cs
rename to osu.Game/Rulesets/Scoring/HitResult.cs
index 961843cbd7..49ab9fd2f0 100644
--- a/osu.Game/Rulesets/Objects/Drawables/HitResult.cs
+++ b/osu.Game/Rulesets/Scoring/HitResult.cs
@@ -3,7 +3,7 @@
using System.ComponentModel;
-namespace osu.Game.Rulesets.Objects.Drawables
+namespace osu.Game.Rulesets.Scoring
{
public enum HitResult
{
diff --git a/osu.Game/Rulesets/Scoring/Score.cs b/osu.Game/Rulesets/Scoring/Score.cs
index 6a06f364c6..025335ba55 100644
--- a/osu.Game/Rulesets/Scoring/Score.cs
+++ b/osu.Game/Rulesets/Scoring/Score.cs
@@ -40,6 +40,6 @@ namespace osu.Game.Rulesets.Scoring
public DateTimeOffset Date;
- public Dictionary Statistics = new Dictionary();
+ public Dictionary Statistics = new Dictionary();
}
}
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index e129a81116..23c4464bb1 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -8,7 +8,6 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI;
-using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Scoring
{
diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs
index 5ca3d9521b..90f63aeab6 100644
--- a/osu.Game/Rulesets/UI/ModIcon.cs
+++ b/osu.Game/Rulesets/UI/ModIcon.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.UI
private readonly ModType type;
- public string TooltipText { get; }
+ public virtual string TooltipText { get; }
public ModIcon(Mod mod)
{
diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs
index fe7c0c05ed..40a37c689b 100644
--- a/osu.Game/Rulesets/UI/RulesetContainer.cs
+++ b/osu.Game/Rulesets/UI/RulesetContainer.cs
@@ -260,6 +260,9 @@ namespace osu.Game.Rulesets.UI
}
Playfield.PostProcess();
+
+ foreach (var mod in Mods.OfType())
+ mod.ApplyToDrawableHitObjects(Playfield.HitObjects.Objects);
}
protected override void Update()
diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs
index 367cf4337d..b2308aca71 100644
--- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs
+++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs
@@ -47,6 +47,8 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
if (Beatmap.Value == null)
return;
+ if (Beatmap.Value.Track.Length == double.PositiveInfinity) return;
+
float markerPos = MathHelper.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth);
seekTo(markerPos / DrawWidth * Beatmap.Value.Track.Length);
}
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 19d00f3477..76f51d1c33 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Screens.Edit
{
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
- public override bool ShowOverlays => false;
+ public override bool ShowOverlaysOnEnter => false;
private readonly Box bottomBackground;
private readonly Container screenContainer;
diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs
index ec2e8e0cb1..c96194f63d 100644
--- a/osu.Game/Screens/Loader.cs
+++ b/osu.Game/Screens/Loader.cs
@@ -17,7 +17,7 @@ namespace osu.Game.Screens
{
private bool showDisclaimer;
- public override bool ShowOverlays => false;
+ public override bool ShowOverlaysOnEnter => false;
public Loader()
{
diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs
index ce7856c5a9..c82d90d16c 100644
--- a/osu.Game/Screens/Menu/ButtonSystem.cs
+++ b/osu.Game/Screens/Menu/ButtonSystem.cs
@@ -11,12 +11,12 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
-using osu.Game.Overlays.Toolbar;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio;
+using osu.Framework.Configuration;
using osu.Framework.Threading;
namespace osu.Game.Screens.Menu
@@ -25,6 +25,8 @@ namespace osu.Game.Screens.Menu
{
public event Action StateChanged;
+ private readonly BindableBool showOverlays = new BindableBool();
+
public Action OnEdit;
public Action OnExit;
public Action OnDirect;
@@ -34,8 +36,6 @@ namespace osu.Game.Screens.Menu
public Action OnChart;
public Action OnTest;
- private Toolbar toolbar;
-
private readonly FlowContainerWithOrigin buttonFlow;
//todo: make these non-internal somehow.
@@ -131,9 +131,9 @@ namespace osu.Game.Screens.Menu
}
[BackgroundDependencyLoader(true)]
- private void load(AudioManager audio, OsuGame game = null)
+ private void load(AudioManager audio, OsuGame game)
{
- toolbar = game?.Toolbar;
+ if (game != null) showOverlays.BindTo(game.ShowOverlays);
sampleBack = audio.Sample.Get(@"Menu/button-back-select");
}
@@ -300,7 +300,7 @@ namespace osu.Game.Screens.Menu
logoDelayedAction = Scheduler.AddDelayed(() =>
{
- toolbar?.Hide();
+ showOverlays.Value = false;
logo.ClearTransforms(targetMember: nameof(Position));
logo.RelativePositionAxes = Axes.Both;
@@ -329,7 +329,7 @@ namespace osu.Game.Screens.Menu
logoTracking = true;
logo.Impact();
- toolbar?.Show();
+ showOverlays.Value = true;
}, 200);
break;
default:
diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs
index 532ee71b72..d0ad613640 100644
--- a/osu.Game/Screens/Menu/Disclaimer.cs
+++ b/osu.Game/Screens/Menu/Disclaimer.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Screens.Menu
private readonly SpriteIcon icon;
private Color4 iconColour;
- public override bool ShowOverlays => false;
+ public override bool ShowOverlaysOnEnter => false;
public override bool HasLocalCursorDisplayed => true;
diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs
index d7beb34a2f..a6a1afa320 100644
--- a/osu.Game/Screens/Menu/Intro.cs
+++ b/osu.Game/Screens/Menu/Intro.cs
@@ -33,7 +33,7 @@ namespace osu.Game.Screens.Menu
public override bool HasLocalCursorDisplayed => true;
- public override bool ShowOverlays => false;
+ public override bool ShowOverlaysOnEnter => false;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenEmpty();
diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs
index 90f68ba9f1..fac0ec1422 100644
--- a/osu.Game/Screens/Menu/MainMenu.cs
+++ b/osu.Game/Screens/Menu/MainMenu.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Screens.Menu
{
private readonly ButtonSystem buttons;
- public override bool ShowOverlays => buttons.State != MenuState.Initial;
+ public override bool ShowOverlaysOnEnter => buttons.State != MenuState.Initial;
private readonly BackgroundScreenDefault background;
private Screen songSelect;
diff --git a/osu.Game/Screens/Multiplayer/ParticipantInfo.cs b/osu.Game/Screens/Multiplayer/ParticipantInfo.cs
index fa48287ce1..2197b7477c 100644
--- a/osu.Game/Screens/Multiplayer/ParticipantInfo.cs
+++ b/osu.Game/Screens/Multiplayer/ParticipantInfo.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Screens.Multiplayer
set
{
host.Text = value.Username;
- flagContainer.Children = new[] { new DrawableFlag(value.Country?.FlagName) { RelativeSizeAxes = Axes.Both } };
+ flagContainer.Children = new[] { new DrawableFlag(value.Country) { RelativeSizeAxes = Axes.Both } };
}
}
diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs
index 4a27c7f1ea..0013d1a882 100644
--- a/osu.Game/Screens/OsuScreen.cs
+++ b/osu.Game/Screens/OsuScreen.cs
@@ -28,7 +28,12 @@ namespace osu.Game.Screens
///
protected virtual BackgroundScreen CreateBackground() => null;
- public virtual bool ShowOverlays => true;
+ protected BindableBool ShowOverlays = new BindableBool();
+
+ ///
+ /// Whether overlays should be shown when this screen is entered or resumed.
+ ///
+ public virtual bool ShowOverlaysOnEnter => true;
protected new OsuGameBase Game => base.Game as OsuGameBase;
@@ -70,7 +75,10 @@ namespace osu.Game.Screens
}
if (osuGame != null)
+ {
Ruleset.BindTo(osuGame.Ruleset);
+ ShowOverlays.BindTo(osuGame.ShowOverlays);
+ }
sampleExit = audio.Sample.Get(@"UI/screen-back");
}
@@ -94,6 +102,8 @@ namespace osu.Game.Screens
base.OnResuming(last);
logo.AppendAnimatingAction(() => LogoArriving(logo, true), true);
sampleExit?.Play();
+
+ ShowOverlays.Value = ShowOverlaysOnEnter;
}
protected override void OnSuspending(Screen next)
@@ -139,6 +149,8 @@ namespace osu.Game.Screens
logo.AppendAnimatingAction(() => LogoArriving(logo, false), true);
base.OnEntering(last);
+
+ ShowOverlays.Value = ShowOverlaysOnEnter;
}
protected override bool OnExiting(Screen next)
diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs
index 6969cd915b..094c0331f4 100644
--- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs
+++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs
@@ -263,6 +263,14 @@ namespace osu.Game.Screens.Play
private class Button : DialogButton
{
+ protected override bool OnHover(InputState state) => true;
+
+ protected override bool OnMouseMove(InputState state)
+ {
+ Selected.Value = true;
+ return base.OnMouseMove(state);
+ }
+
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Repeat || args.Key != Key.Enter || !Selected)
diff --git a/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs b/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs
index 06ef87276a..351db533f3 100644
--- a/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs
+++ b/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs
@@ -7,10 +7,10 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Screens.Play.HUD
{
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index 340fc39d52..35f39e940f 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -35,7 +35,7 @@ namespace osu.Game.Screens.Play
{
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
- public override bool ShowOverlays => false;
+ public override bool ShowOverlaysOnEnter => false;
public override bool HasLocalCursorDisplayed => !pauseContainer.IsPaused && !HasFailed && RulesetContainer.ProvidingUserCursor;
@@ -46,6 +46,8 @@ namespace osu.Game.Screens.Play
public bool HasFailed { get; private set; }
public bool AllowPause { get; set; } = true;
+ public bool AllowLeadIn { get; set; } = true;
+ public bool AllowResults { get; set; } = true;
public int RestartCount;
@@ -125,7 +127,7 @@ namespace osu.Game.Screens.Play
}
catch (Exception e)
{
- Logger.Log($"Could not load this beatmap sucessfully ({e})!", LoggingTarget.Runtime, LogLevel.Error);
+ Logger.Error(e, "Could not load beatmap sucessfully!");
//couldn't load, hard abort!
Exit();
@@ -136,7 +138,10 @@ namespace osu.Game.Screens.Play
decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
var firstObjectTime = RulesetContainer.Objects.First().StartTime;
- decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn)));
+ decoupledClock.Seek(AllowLeadIn
+ ? Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn))
+ : firstObjectTime);
+
decoupledClock.ProcessFrame();
offsetClock = new FramedOffsetClock(decoupledClock);
@@ -273,6 +278,8 @@ namespace osu.Game.Screens.Play
ValidForResume = false;
+ if (!AllowResults) return;
+
using (BeginDelayedSequence(1000))
{
onCompletionEvent = Schedule(delegate
diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs
index de67bef004..15a97096e7 100644
--- a/osu.Game/Screens/Play/PlayerLoader.cs
+++ b/osu.Game/Screens/Play/PlayerLoader.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Screens.Play
private BeatmapMetadataDisplay info;
private bool showOverlays = true;
- public override bool ShowOverlays => showOverlays;
+ public override bool ShowOverlaysOnEnter => showOverlays;
public override bool AllowBeatmapRulesetChange => false;
@@ -250,7 +250,7 @@ namespace osu.Game.Screens.Play
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
},
- new MetadataLine("Mapper", metadata.Author.Username)
+ new MetadataLine("Mapper", metadata.AuthorString)
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs
index 8e27cb235c..406887624c 100644
--- a/osu.Game/Screens/Ranking/Results.cs
+++ b/osu.Game/Screens/Ranking/Results.cs
@@ -17,6 +17,7 @@ using OpenTK.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics.Sprites;
namespace osu.Game.Screens.Ranking
{
@@ -183,7 +184,7 @@ namespace osu.Game.Screens.Ranking
Height = 50,
Margin = new MarginPadding { Bottom = 110 },
},
- new SpriteText
+ new OsuSpriteText
{
Text = $"{score.MaxCombo}x",
TextSize = 40,
@@ -194,7 +195,7 @@ namespace osu.Game.Screens.Ranking
Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomCentre,
},
- new SpriteText
+ new OsuSpriteText
{
Text = "max combo",
TextSize = 20,
@@ -204,7 +205,7 @@ namespace osu.Game.Screens.Ranking
Anchor = Anchor.CentreLeft,
Origin = Anchor.TopCentre,
},
- new SpriteText
+ new OsuSpriteText
{
Text = $"{score.Accuracy:P2}",
TextSize = 40,
@@ -215,7 +216,7 @@ namespace osu.Game.Screens.Ranking
Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomCentre,
},
- new SpriteText
+ new OsuSpriteText
{
Text = "accuracy",
TextSize = 20,
diff --git a/osu.Game/Screens/Ranking/ResultsPageScore.cs b/osu.Game/Screens/Ranking/ResultsPageScore.cs
index 911b688669..0d29b74dfd 100644
--- a/osu.Game/Screens/Ranking/ResultsPageScore.cs
+++ b/osu.Game/Screens/Ranking/ResultsPageScore.cs
@@ -23,6 +23,7 @@ using osu.Game.Screens.Play;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Extensions;
namespace osu.Game.Screens.Ranking
{
@@ -163,7 +164,7 @@ namespace osu.Game.Screens.Ranking
}
};
- statisticsContainer.ChildrenEnumerable = Score.Statistics.Select(s => new DrawableScoreStatistic(s));
+ statisticsContainer.ChildrenEnumerable = Score.Statistics.OrderByDescending(p => p.Key).Select(s => new DrawableScoreStatistic(s));
}
protected override void LoadComplete()
@@ -186,9 +187,9 @@ namespace osu.Game.Screens.Ranking
private class DrawableScoreStatistic : Container
{
- private readonly KeyValuePair statistic;
+ private readonly KeyValuePair statistic;
- public DrawableScoreStatistic(KeyValuePair statistic)
+ public DrawableScoreStatistic(KeyValuePair statistic)
{
this.statistic = statistic;
@@ -201,15 +202,15 @@ namespace osu.Game.Screens.Ranking
{
Children = new Drawable[]
{
- new SpriteText {
+ new OsuSpriteText {
Text = statistic.Value.ToString().PadLeft(4, '0'),
Colour = colours.Gray7,
TextSize = 30,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
- new SpriteText {
- Text = statistic.Key,
+ new OsuSpriteText {
+ Text = statistic.Key.GetDescription(),
Colour = colours.Gray7,
Font = @"Exo2.0-Bold",
Y = 26,
@@ -250,16 +251,16 @@ namespace osu.Game.Screens.Ranking
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
- Text = datetime.ToString("HH:mm"),
- Padding = new MarginPadding { Left = 10, Right = 10, Top = 5, Bottom = 5 },
+ Text = datetime.ToShortDateString(),
+ Padding = new MarginPadding { Horizontal = 10, Vertical = 5 },
Colour = Color4.White,
},
new OsuSpriteText
{
Origin = Anchor.CentreRight,
Anchor = Anchor.CentreRight,
- Text = datetime.ToString("yyyy/MM/dd"),
- Padding = new MarginPadding { Left = 10, Right = 10, Top = 5, Bottom = 5 },
+ Text = datetime.ToShortTimeString(),
+ Padding = new MarginPadding { Horizontal = 10, Vertical = 5 },
Colour = Color4.White,
}
};
@@ -324,7 +325,14 @@ namespace osu.Game.Screens.Ranking
title.Colour = artist.Colour = colours.BlueDarker;
versionMapper.Colour = colours.Gray8;
- versionMapper.Text = $"{beatmap.Version} - mapped by {beatmap.Metadata.Author.Username}";
+ var creator = beatmap.Metadata.Author?.Username;
+ if (!string.IsNullOrEmpty(creator)) {
+ versionMapper.Text = $"mapped by {creator}";
+
+ if (!string.IsNullOrEmpty(beatmap.Version))
+ versionMapper.Text = $"{beatmap.Version} - " + versionMapper.Text;
+ }
+
title.Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title);
artist.Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist);
}
diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs
index be176c1459..b343998e11 100644
--- a/osu.Game/Screens/Select/BeatmapCarousel.cs
+++ b/osu.Game/Screens/Select/BeatmapCarousel.cs
@@ -53,6 +53,11 @@ namespace osu.Game.Screens.Select
public override bool HandleInput => AllowSelection;
+ ///
+ /// Used to avoid firing null selections before the initial beatmaps have been loaded via .
+ ///
+ private bool initialLoadComplete;
+
private IEnumerable beatmapSets => root.Children.OfType();
public IEnumerable BeatmapSets
@@ -75,7 +80,12 @@ namespace osu.Game.Screens.Select
scrollableContent.Clear(false);
itemsCache.Invalidate();
scrollPositionCache.Invalidate();
- BeatmapSetsChanged?.Invoke();
+
+ Schedule(() =>
+ {
+ BeatmapSetsChanged?.Invoke();
+ initialLoadComplete = true;
+ });
}));
}
}
@@ -142,7 +152,6 @@ namespace osu.Game.Screens.Select
if (newSet == null)
{
itemsCache.Invalidate();
- SelectNext();
return;
}
@@ -155,6 +164,7 @@ namespace osu.Game.Screens.Select
select((CarouselItem)newSet.Beatmaps.FirstOrDefault(b => b.Beatmap.ID == selectedBeatmap?.Beatmap.ID) ?? newSet);
itemsCache.Invalidate();
+ Schedule(() => BeatmapSetsChanged?.Invoke());
});
}
@@ -184,7 +194,14 @@ namespace osu.Game.Screens.Select
if (!Items.Any())
return;
- int originalIndex = Items.IndexOf(selectedBeatmap?.Drawables.First());
+ DrawableCarouselItem drawable = null;
+
+ if (selectedBeatmap != null && (drawable = selectedBeatmap.Drawables.FirstOrDefault()) == null)
+ // if the selected beatmap isn't present yet, we can't correctly change selection.
+ // we can fix this by changing this method to not reference drawables / Items in the first place.
+ return;
+
+ int originalIndex = Items.IndexOf(drawable);
int currentIndex = originalIndex;
// local function to increment the index in the required direction, wrapping over extremities.
@@ -512,7 +529,7 @@ namespace osu.Game.Screens.Select
currentY += DrawHeight / 2;
scrollableContent.Height = currentY;
- if (selectedBeatmapSet != null && selectedBeatmapSet.State.Value != CarouselItemState.Selected)
+ if (initialLoadComplete && (selectedBeatmapSet == null || selectedBeatmap == null || selectedBeatmapSet.State.Value != CarouselItemState.Selected))
{
selectedBeatmapSet = null;
SelectionChanged?.Invoke(null);
diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs
index a9a778fe17..79d76dd00e 100644
--- a/osu.Game/Screens/Select/BeatmapDetails.cs
+++ b/osu.Game/Screens/Select/BeatmapDetails.cs
@@ -17,6 +17,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Screens.Select.Details;
using osu.Game.Beatmaps;
+using osu.Game.Graphics.Containers;
namespace osu.Game.Screens.Select
{
@@ -334,7 +335,7 @@ namespace osu.Game.Screens.Select
TextSize = 14,
},
},
- textFlow = new TextFlowContainer
+ textFlow = new OsuTextFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
@@ -359,7 +360,7 @@ namespace osu.Game.Screens.Select
private void setTextAsync(string text)
{
- LoadComponentAsync(new TextFlowContainer(s => s.TextSize = 14)
+ LoadComponentAsync(new OsuTextFlowContainer(s => s.TextSize = 14)
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
index 3ef6ceeaeb..729cb458c2 100644
--- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs
+++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Screens.Select
{
private static readonly Vector2 wedged_container_shear = new Vector2(0.15f, 0);
- private Drawable info;
+ protected BufferedWedgeInfo Info;
public BeatmapInfoWedge()
{
@@ -35,6 +35,7 @@ namespace osu.Game.Screens.Select
Masking = true;
BorderColour = new Color4(221, 255, 255, 255);
BorderThickness = 2.5f;
+ Alpha = 0;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
@@ -50,12 +51,14 @@ namespace osu.Game.Screens.Select
{
this.MoveToX(0, 800, Easing.OutQuint);
this.RotateTo(0, 800, Easing.OutQuint);
+ this.FadeIn(250);
}
protected override void PopOut()
{
- this.MoveToX(-100, 800, Easing.InQuint);
- this.RotateTo(10, 800, Easing.InQuint);
+ this.MoveToX(-100, 800, Easing.In);
+ this.RotateTo(10, 800, Easing.In);
+ this.FadeOut(500, Easing.In);
}
public void UpdateBeatmap(WorkingBeatmap beatmap)
@@ -63,23 +66,26 @@ namespace osu.Game.Screens.Select
LoadComponentAsync(new BufferedWedgeInfo(beatmap)
{
Shear = -Shear,
- Depth = info?.Depth + 1 ?? 0,
+ Depth = Info?.Depth + 1 ?? 0,
}, newInfo =>
{
- // ensure we ourselves are visible if not already.
- if (!IsPresent)
- this.FadeIn(250);
+ State = beatmap == null ? Visibility.Hidden : Visibility.Visible;
- info?.FadeOut(250);
- info?.Expire();
+ Info?.FadeOut(250);
+ Info?.Expire();
- Add(info = newInfo);
+ Add(Info = newInfo);
});
}
public class BufferedWedgeInfo : BufferedContainer
{
private readonly WorkingBeatmap working;
+ public OsuSpriteText VersionLabel { get; private set; }
+ public OsuSpriteText TitleLabel { get; private set; }
+ public OsuSpriteText ArtistLabel { get; private set; }
+ public FillFlowContainer MapperContainer { get; private set; }
+ public FillFlowContainer InfoLabelContainer { get; private set; }
public BufferedWedgeInfo(WorkingBeatmap working)
{
@@ -89,34 +95,8 @@ namespace osu.Game.Screens.Select
[BackgroundDependencyLoader]
private void load()
{
- BeatmapInfo beatmapInfo = working.BeatmapInfo;
- BeatmapMetadata metadata = beatmapInfo.Metadata ?? working.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
- Beatmap beatmap = working.Beatmap;
-
- List labels = new List();
-
- if (beatmap != null)
- {
- HitObject lastObject = beatmap.HitObjects.LastOrDefault();
- double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0;
-
- labels.Add(new InfoLabel(new BeatmapStatistic
- {
- Name = "Length",
- Icon = FontAwesome.fa_clock_o,
- Content = beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(endTime - beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"),
- }));
-
- labels.Add(new InfoLabel(new BeatmapStatistic
- {
- Name = "BPM",
- Icon = FontAwesome.fa_circle,
- Content = getBPMRange(beatmap),
- }));
-
- //get statistics from the current ruleset.
- labels.AddRange(beatmapInfo.Ruleset.CreateInstance().GetBeatmapStatistics(working).Select(s => new InfoLabel(s)));
- }
+ var beatmapInfo = working.BeatmapInfo;
+ var metadata = beatmapInfo.Metadata ?? working.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
PixelSnapping = true;
CacheDrawnFrameBuffer = true;
@@ -165,7 +145,7 @@ namespace osu.Game.Screens.Select
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
- new OsuSpriteText
+ VersionLabel = new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
Text = beatmapInfo.Version,
@@ -175,69 +155,112 @@ namespace osu.Game.Screens.Select
},
new FillFlowContainer
{
- Name = "Bottom-aligned metadata",
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
+ Name = "Centre-aligned metadata",
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.TopLeft,
+ Y = -22,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = 15, Left = 25, Right = 10, Bottom = 20 },
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
- new OsuSpriteText
+ TitleLabel = new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
- Text = !string.IsNullOrEmpty(metadata.Source) ? metadata.Source + " — " + metadata.Title : metadata.Title,
+ Text = string.IsNullOrEmpty(metadata.Source) ? metadata.Title : metadata.Source + " — " + metadata.Title,
TextSize = 28,
},
- new OsuSpriteText
+ ArtistLabel = new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
Text = metadata.Artist,
TextSize = 17,
},
- new FillFlowContainer
+ MapperContainer = new FillFlowContainer
{
Margin = new MarginPadding { Top = 10 },
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
- Children = new[]
- {
- new OsuSpriteText
- {
- Font = @"Exo2.0-Medium",
- Text = "mapped by ",
- TextSize = 15,
- },
- new OsuSpriteText
- {
- Font = @"Exo2.0-Bold",
- Text = metadata.Author.Username,
- TextSize = 15,
- },
- }
+ Children = getMapper(metadata)
},
- new FillFlowContainer
+ InfoLabelContainer = new FillFlowContainer
{
Margin = new MarginPadding { Top = 20 },
Spacing = new Vector2(20, 0),
AutoSizeAxes = Axes.Both,
- Children = labels
- },
+ Children = getInfoLabels()
+ }
}
- },
+ }
};
}
+ private InfoLabel[] getInfoLabels()
+ {
+ var beatmap = working.Beatmap;
+ var info = working.BeatmapInfo;
+
+ List labels = new List();
+
+ if (beatmap?.HitObjects?.Count > 0)
+ {
+ HitObject lastObject = beatmap.HitObjects.LastOrDefault();
+ double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0;
+
+ labels.Add(new InfoLabel(new BeatmapStatistic
+ {
+ Name = "Length",
+ Icon = FontAwesome.fa_clock_o,
+ Content = beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(endTime - beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"),
+ }));
+
+ labels.Add(new InfoLabel(new BeatmapStatistic
+ {
+ Name = "BPM",
+ Icon = FontAwesome.fa_circle,
+ Content = getBPMRange(beatmap),
+ }));
+
+ //get statistics from the current ruleset.
+ labels.AddRange(info.Ruleset.CreateInstance().GetBeatmapStatistics(working).Select(s => new InfoLabel(s)));
+ }
+
+ return labels.ToArray();
+ }
+
private string getBPMRange(Beatmap beatmap)
{
double bpmMax = beatmap.ControlPointInfo.BPMMaximum;
double bpmMin = beatmap.ControlPointInfo.BPMMinimum;
- if (Precision.AlmostEquals(bpmMin, bpmMax)) return $"{bpmMin:0}";
+ if (Precision.AlmostEquals(bpmMin, bpmMax))
+ return $"{bpmMin:0}";
return $"{bpmMin:0}-{bpmMax:0} (mostly {beatmap.ControlPointInfo.BPMMode:0})";
}
+ private OsuSpriteText[] getMapper(BeatmapMetadata metadata)
+ {
+ if (string.IsNullOrEmpty(metadata.Author?.Username))
+ return Array.Empty();
+
+ return new[]
+ {
+ new OsuSpriteText
+ {
+ Font = @"Exo2.0-Medium",
+ Text = "mapped by ",
+ TextSize = 15,
+ },
+ new OsuSpriteText
+ {
+ Font = @"Exo2.0-Bold",
+ Text = metadata.Author.Username,
+ TextSize = 15,
+ }
+ };
+ }
+
public class InfoLabel : Container, IHasTooltip
{
public string TooltipText { get; private set; }
diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
index 6c0cc341fd..cea658b06c 100644
--- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
+++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
@@ -46,7 +46,7 @@ namespace osu.Game.Screens.Select.Carousel
{
if (songSelect != null)
{
- startRequested = songSelect.Start;
+ startRequested = songSelect.FinaliseSelection;
editRequested = songSelect.Edit;
}
diff --git a/osu.Game/Screens/Select/EditSongSelect.cs b/osu.Game/Screens/Select/EditSongSelect.cs
index f02d25501e..37f2663d91 100644
--- a/osu.Game/Screens/Select/EditSongSelect.cs
+++ b/osu.Game/Screens/Select/EditSongSelect.cs
@@ -7,6 +7,10 @@ namespace osu.Game.Screens.Select
{
protected override bool ShowFooter => false;
- protected override void Start() => Exit();
+ protected override bool OnSelectionFinalised()
+ {
+ Exit();
+ return true;
+ }
}
}
diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs
index 1b86cec613..2dbd63f77b 100644
--- a/osu.Game/Screens/Select/FilterControl.cs
+++ b/osu.Game/Screens/Select/FilterControl.cs
@@ -75,7 +75,7 @@ namespace osu.Game.Screens.Select
{
Children = new Drawable[]
{
- new Box
+ Background = new Box
{
Colour = Color4.Black,
Alpha = 0.8f,
@@ -167,6 +167,8 @@ namespace osu.Game.Screens.Select
private Bindable showConverted;
+ public readonly Box Background;
+
[BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuColour colours, OsuGame osu, OsuConfigManager config)
{
diff --git a/osu.Game/Screens/Select/ImportFromStablePopup.cs b/osu.Game/Screens/Select/ImportFromStablePopup.cs
new file mode 100644
index 0000000000..03e9462636
--- /dev/null
+++ b/osu.Game/Screens/Select/ImportFromStablePopup.cs
@@ -0,0 +1,33 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Game.Graphics;
+using osu.Game.Overlays.Dialog;
+
+namespace osu.Game.Screens.Select
+{
+ public class ImportFromStablePopup : PopupDialog
+ {
+ public ImportFromStablePopup(Action importFromStable)
+ {
+ HeaderText = @"You have no beatmaps!";
+ BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps?";
+
+ Icon = FontAwesome.fa_plane;
+
+ Buttons = new PopupDialogButton[]
+ {
+ new PopupDialogOkButton
+ {
+ Text = @"Yes please!",
+ Action = importFromStable
+ },
+ new PopupDialogCancelButton
+ {
+ Text = @"No, I'd like to start from scratch",
+ },
+ };
+ }
+ }
+}
diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
index 03466439ad..e0c9a3e04e 100644
--- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
@@ -145,7 +145,7 @@ namespace osu.Game.Screens.Select.Leaderboards
Masking = true,
Children = new Drawable[]
{
- new DrawableFlag(Score.User?.Country?.FlagName)
+ new DrawableFlag(Score.User?.Country)
{
Width = 30,
RelativeSizeAxes = Axes.Y,
diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs
index 898c195432..9143da326d 100644
--- a/osu.Game/Screens/Select/MatchSongSelect.cs
+++ b/osu.Game/Screens/Select/MatchSongSelect.cs
@@ -5,6 +5,10 @@ namespace osu.Game.Screens.Select
{
public class MatchSongSelect : SongSelect
{
- protected override void Start() => Exit();
+ protected override bool OnSelectionFinalised()
+ {
+ Exit();
+ return true;
+ }
}
}
diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs
index 4a0ee31fbb..87b3485dc1 100644
--- a/osu.Game/Screens/Select/PlaySongSelect.cs
+++ b/osu.Game/Screens/Select/PlaySongSelect.cs
@@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
+using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Play;
@@ -45,8 +46,8 @@ namespace osu.Game.Screens.Select
private SampleChannel sampleConfirm;
- [BackgroundDependencyLoader]
- private void load(OsuColour colours, AudioManager audio)
+ [BackgroundDependencyLoader(true)]
+ private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay)
{
sampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection");
@@ -59,6 +60,16 @@ namespace osu.Game.Screens.Select
ValidForResume = false;
Push(new Editor());
}, Key.Number3);
+
+ if (dialogOverlay != null)
+ {
+ Schedule(() =>
+ {
+ // if we have no beatmaps but osu-stable is found, let's prompt the user to import.
+ if (!beatmaps.GetAllUsableBeatmapSets().Any() && beatmaps.StableInstallationAvailable)
+ dialogOverlay.Push(new ImportFromStablePopup(() => beatmaps.ImportFromStable()));
+ });
+ }
}
protected override void UpdateBeatmap(WorkingBeatmap beatmap)
@@ -113,9 +124,9 @@ namespace osu.Game.Screens.Select
return false;
}
- protected override void Start()
+ protected override bool OnSelectionFinalised()
{
- if (player != null) return;
+ if (player != null) return false;
// Ctrl+Enter should start map with autoplay enabled.
if (GetContainingInputManager().CurrentState?.Keyboard.ControlPressed == true)
@@ -136,7 +147,12 @@ namespace osu.Game.Screens.Select
sampleConfirm?.Play();
- LoadComponentAsync(player = new PlayerLoader(new Player()), l => Push(player));
+ LoadComponentAsync(player = new PlayerLoader(new Player()), l =>
+ {
+ if (IsCurrentScreen) Push(player);
+ });
+
+ return true;
}
}
}
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 68ee08e721..b89a8a4e73 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -55,7 +55,7 @@ namespace osu.Game.Screens.Select
protected Container LeftContent;
- private readonly BeatmapCarousel carousel;
+ protected readonly BeatmapCarousel Carousel;
private readonly BeatmapInfoWedge beatmapInfoWedge;
private DialogOverlay dialogOverlay;
private BeatmapManager beatmaps;
@@ -103,25 +103,44 @@ namespace osu.Game.Screens.Select
Right = left_area_padding * 2,
}
},
- carousel = new BeatmapCarousel
+ new Container
{
- RelativeSizeAxes = Axes.Y,
- Size = new Vector2(carousel_width, 1),
- Anchor = Anchor.CentreRight,
- Origin = Anchor.CentreRight,
- SelectionChanged = carouselSelectionChanged,
- BeatmapSetsChanged = carouselBeatmapsLoaded,
- },
- FilterControl = new FilterControl
- {
- RelativeSizeAxes = Axes.X,
- Height = filter_height,
- FilterChanged = c => carousel.Filter(c),
- Exit = Exit,
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Width = 2, //avoid horizontal masking so the panels don't clip when screen stack is pushed.
+ Child = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Width = 0.5f,
+ Children = new Drawable[]
+ {
+ Carousel = new BeatmapCarousel
+ {
+ Masking = false,
+ RelativeSizeAxes = Axes.Y,
+ Size = new Vector2(carousel_width, 1),
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ SelectionChanged = carouselSelectionChanged,
+ BeatmapSetsChanged = carouselBeatmapsLoaded,
+ },
+ FilterControl = new FilterControl
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = filter_height,
+ FilterChanged = c => Carousel.Filter(c),
+ Background = { Width = 2 },
+ Exit = Exit,
+ },
+ }
+ },
},
beatmapInfoWedge = new BeatmapInfoWedge
{
- Alpha = 0,
Size = wedged_container_size,
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding
@@ -130,7 +149,7 @@ namespace osu.Game.Screens.Select
Right = left_area_padding,
},
},
- new ResetScrollContainer(() => carousel.ScrollToSelected())
+ new ResetScrollContainer(() => Carousel.ScrollToSelected())
{
RelativeSizeAxes = Axes.Y,
Width = 250,
@@ -190,15 +209,15 @@ namespace osu.Game.Screens.Select
initialAddSetsTask = new CancellationTokenSource();
- carousel.BeatmapSets = this.beatmaps.GetAllUsableBeatmapSets();
+ Carousel.BeatmapSets = this.beatmaps.GetAllUsableBeatmapSets();
- Beatmap.DisabledChanged += disabled => carousel.AllowSelection = !disabled;
+ Beatmap.DisabledChanged += disabled => Carousel.AllowSelection = !disabled;
Beatmap.TriggerChange();
Beatmap.ValueChanged += b =>
{
if (IsCurrentScreen)
- carousel.SelectBeatmap(b?.BeatmapInfo);
+ Carousel.SelectBeatmap(b?.BeatmapInfo);
};
}
@@ -208,13 +227,18 @@ namespace osu.Game.Screens.Select
Push(new Editor());
}
- public void Start(BeatmapInfo beatmap)
+ ///
+ /// Call to make a selection and perform the default action for this SongSelect.
+ ///
+ /// An optional beatmap to override the current carousel selection.
+ public void FinaliseSelection(BeatmapInfo beatmap = null)
{
// if we have a pending filter operation, we want to run it now.
// it could change selection (ie. if the ruleset has been changed).
- carousel.FlushPendingFilterOperations();
+ Carousel.FlushPendingFilterOperations();
- carousel.SelectBeatmap(beatmap);
+ if (beatmap != null)
+ Carousel.SelectBeatmap(beatmap);
if (selectionChangedDebounce?.Completed == false)
{
@@ -223,13 +247,14 @@ namespace osu.Game.Screens.Select
selectionChangedDebounce = null;
}
- Start();
+ OnSelectionFinalised();
}
///
/// Called when a selection is made.
///
- protected abstract void Start();
+ /// If a resultant action occurred that takes the user away from SongSelect.
+ protected abstract bool OnSelectionFinalised();
private ScheduledDelegate selectionChangedDebounce;
@@ -282,9 +307,9 @@ namespace osu.Game.Screens.Select
private void triggerRandom()
{
if (GetContainingInputManager().CurrentState.Keyboard.ShiftPressed)
- carousel.SelectPreviousRandom();
+ Carousel.SelectPreviousRandom();
else
- carousel.SelectNextRandom();
+ Carousel.SelectNextRandom();
}
protected override void OnEntering(Screen last)
@@ -320,7 +345,7 @@ namespace osu.Game.Screens.Select
logo.Action = () =>
{
- Start();
+ FinaliseSelection();
return false;
};
}
@@ -399,7 +424,6 @@ namespace osu.Game.Screens.Select
backgroundModeBeatmap.FadeTo(1, 250);
}
- beatmapInfoWedge.State = Visibility.Visible;
beatmapInfoWedge.UpdateBeatmap(beatmap);
}
@@ -417,17 +441,17 @@ namespace osu.Game.Screens.Select
}
}
- private void onBeatmapSetAdded(BeatmapSetInfo s) => carousel.UpdateBeatmapSet(s);
- private void onBeatmapSetRemoved(BeatmapSetInfo s) => carousel.RemoveBeatmapSet(s);
- private void onBeatmapRestored(BeatmapInfo b) => carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
- private void onBeatmapHidden(BeatmapInfo b) => carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
+ private void onBeatmapSetAdded(BeatmapSetInfo s) => Carousel.UpdateBeatmapSet(s);
+ private void onBeatmapSetRemoved(BeatmapSetInfo s) => Carousel.RemoveBeatmapSet(s);
+ private void onBeatmapRestored(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
+ private void onBeatmapHidden(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
private void carouselBeatmapsLoaded()
{
- if (Beatmap.Value.BeatmapSetInfo?.DeletePending == false)
- carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo);
- else
- carousel.SelectNextRandom();
+ if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false)
+ Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo);
+ else if (Carousel.SelectedBeatmapSet == null)
+ Carousel.SelectNextRandom();
}
private void delete(BeatmapSetInfo beatmap)
@@ -444,7 +468,7 @@ namespace osu.Game.Screens.Select
{
case Key.KeypadEnter:
case Key.Enter:
- Start();
+ FinaliseSelection();
return true;
case Key.Delete:
if (state.Keyboard.ShiftPressed)
diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs
index 3e7ab56c99..fbf24eb609 100644
--- a/osu.Game/Screens/Tournament/Drawings.cs
+++ b/osu.Game/Screens/Tournament/Drawings.cs
@@ -29,7 +29,7 @@ namespace osu.Game.Screens.Tournament
{
private const string results_filename = "drawings_results.txt";
- public override bool ShowOverlays => false;
+ public override bool ShowOverlaysOnEnter => false;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault();
diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs
index da139775b1..82248c2cb8 100644
--- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs
+++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs
@@ -19,6 +19,6 @@ namespace osu.Game.Tests.Beatmaps
protected override Beatmap GetBeatmap() => beatmap;
protected override Texture GetBackground() => null;
- protected override Track GetTrack() => null;
+ protected override Track GetTrack() => new TrackVirtual();
}
}
diff --git a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs
index f71bece279..8984fc843f 100644
--- a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs
+++ b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs
@@ -12,7 +12,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
@@ -224,7 +223,7 @@ namespace osu.Game.Tests.Visual
if (!api.IsLoggedIn)
{
- InternalChild = new SpriteText
+ InternalChild = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs
index 106f0fa8f3..933781890f 100644
--- a/osu.Game/Tests/Visual/TestCasePlayer.cs
+++ b/osu.Game/Tests/Visual/TestCasePlayer.cs
@@ -22,6 +22,8 @@ namespace osu.Game.Tests.Visual
protected Player Player;
+ private TestWorkingBeatmap working;
+
///
/// Create a TestCase which runs through the Player screen.
///
@@ -75,7 +77,7 @@ namespace osu.Game.Tests.Visual
var instance = r.CreateInstance();
- WorkingBeatmap working = new TestWorkingBeatmap(beatmap);
+ working = new TestWorkingBeatmap(beatmap);
working.Mods.Value = new[] { instance.GetAllMods().First(m => m is ModNoFail) };
if (Player != null)
@@ -88,10 +90,21 @@ namespace osu.Game.Tests.Visual
return player;
}
+ protected override void Update()
+ {
+ base.Update();
+
+ if (working != null)
+ // note that this will override any mod rate application
+ working.Track.Rate = Clock.Rate;
+ }
+
protected virtual Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset) => new Player
{
InitialBeatmap = beatmap,
- AllowPause = false
+ AllowPause = false,
+ AllowLeadIn = false,
+ AllowResults = false,
};
private const string test_beatmap_data =
diff --git a/osu.Game/Users/Country.cs b/osu.Game/Users/Country.cs
index 0c0d12c1cc..46ddaee637 100644
--- a/osu.Game/Users/Country.cs
+++ b/osu.Game/Users/Country.cs
@@ -6,6 +6,7 @@ using Newtonsoft.Json;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
@@ -26,36 +27,30 @@ namespace osu.Game.Users
public string FlagName;
}
- public class DrawableFlag : Container
+ public class DrawableFlag : Container, IHasTooltip
{
private readonly Sprite sprite;
private TextureStore textures;
- private string flagName;
- public string FlagName
+ private Country country;
+ public Country Country
{
- get { return flagName; }
+ get { return country; }
set
{
- if (value == flagName) return;
- flagName = value;
- sprite.Texture = textures.Get($@"Flags/{flagName}");
+ if (value == country)
+ return;
+
+ country = value;
+ sprite.Texture = getFlagTexture();
}
}
- [BackgroundDependencyLoader]
- private void load(TextureStore ts)
- {
- if (ts == null)
- throw new ArgumentNullException(nameof(ts));
+ public string TooltipText => country?.FullName;
- textures = ts;
- sprite.Texture = textures.Get($@"Flags/{flagName}");
- }
-
- public DrawableFlag(string name = null)
+ public DrawableFlag(Country country = null)
{
- flagName = name ?? @"__";
+ this.country = country;
Children = new Drawable[]
{
@@ -65,5 +60,17 @@ namespace osu.Game.Users
},
};
}
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore ts)
+ {
+ if (ts == null)
+ throw new ArgumentNullException(nameof(ts));
+
+ textures = ts;
+ sprite.Texture = getFlagTexture();
+ }
+
+ private Texture getFlagTexture() => textures.Get($@"Flags/{country?.FlagName ?? @"__"}");
}
}
diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs
index d056afcf54..e0a4e3184d 100644
--- a/osu.Game/Users/UserPanel.cs
+++ b/osu.Game/Users/UserPanel.cs
@@ -16,8 +16,8 @@ using osu.Game.Overlays;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Cursor;
-using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers;
+using osu.Game.Overlays.Profile;
namespace osu.Game.Users
{
@@ -114,7 +114,7 @@ namespace osu.Game.Users
Spacing = new Vector2(5f, 0f),
Children = new Drawable[]
{
- new DrawableFlag(user.Country?.FlagName)
+ new DrawableFlag(user.Country)
{
Width = 30f,
RelativeSizeAxes = Axes.Y,
@@ -220,53 +220,5 @@ namespace osu.Game.Users
{
new OsuMenuItem("View Profile", MenuItemType.Highlighted, ViewProfile),
};
-
- private class SupporterIcon : CircularContainer
- {
- private readonly Box background;
-
- public SupporterIcon()
- {
- Masking = true;
- Children = new Drawable[]
- {
- new Box { RelativeSizeAxes = Axes.Both },
- new CircularContainer
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Both,
- Scale = new Vector2(0.8f),
- Masking = true,
- Children = new Drawable[]
- {
- background = new Box { RelativeSizeAxes = Axes.Both },
- new Triangles
- {
- TriangleScale = 0.2f,
- ColourLight = OsuColour.FromHex(@"ff7db7"),
- ColourDark = OsuColour.FromHex(@"de5b95"),
- RelativeSizeAxes = Axes.Both,
- Velocity = 0.3f,
- },
- }
- },
- new SpriteIcon
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Both,
- Icon = FontAwesome.fa_heart,
- Scale = new Vector2(0.45f),
- }
- };
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- background.Colour = colours.Pink;
- }
- }
}
}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 94678106bf..82b9f41567 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -143,8 +143,8 @@
$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll
True
-
- $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
+
+ $(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll
True
@@ -239,7 +239,6 @@
-
@@ -268,6 +267,7 @@
+
@@ -310,6 +310,8 @@
+
+
@@ -610,7 +612,7 @@
-
+
diff --git a/osu.Game/packages.config b/osu.Game/packages.config
index 02ace918de..e35f06dd67 100644
--- a/osu.Game/packages.config
+++ b/osu.Game/packages.config
@@ -66,7 +66,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
-
+
diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings
index 2f52881d6d..20007e3306 100644
--- a/osu.sln.DotSettings
+++ b/osu.sln.DotSettings
@@ -172,6 +172,7 @@
NEXT_LINE
NEXT_LINE
True
+ NEVER
False
False
True
@@ -655,7 +656,11 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-frame
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
True
+ True
+ True
+ True
True
True
+ True
True
True