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.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs
index 0780ed9cce..4bf0b76814 100644
--- a/osu.Desktop/Overlays/VersionManager.cs
+++ b/osu.Desktop/Overlays/VersionManager.cs
@@ -118,7 +118,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.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/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.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/Mods/ManiaMod.cs b/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs
index dfc9993bde..61e11f7610 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs
@@ -105,6 +105,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public abstract class ManiaKeyMod : Mod
{
+ // TODO: implement using the IApplicable interface. Haven't done so yet because KeyCount isn't even hooked up at the moment.
+
public override string ShortenedName => Name;
public abstract int KeyCount { get; }
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
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.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..72ca9b37a8 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,12 +21,12 @@ 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;
Position = HitObject.StackedPosition;
- Scale = new Vector2(HitObject.Scale);
+ Scale = new Vector2(h.Scale);
Children = new Drawable[]
{
@@ -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..f32a027f2e 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,30 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateCurrentState(ArmedState state)
{
- ball.FadeIn();
+ Ball.FadeIn();
+ Ball.ScaleTo(HitObject.Scale);
using (BeginDelayedSequence(slider.Duration, true))
{
- body.FadeOut(160);
- ball.FadeOut(160);
+ const float fade_out_time = 450;
- this.FadeOut(800)
- .Expire();
+ // intentionally pile on an extra FadeOut to make it happen much faster.
+ Ball.FadeOut(fade_out_time / 4, Easing.Out);
+
+ switch (state)
+ {
+ case ArmedState.Hit:
+ Ball.ScaleTo(HitObject.Scale * 1.4f, fade_out_time, Easing.Out);
+ break;
+ }
+
+ this.FadeOut(fade_out_time, Easing.OutQuint).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..bbe6b3a0a0 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -13,20 +13,21 @@ 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
{
public class DrawableSpinner : DrawableOsuHitObject
{
- private readonly Spinner spinner;
+ protected 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;
@@ -49,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
// we are slightly bigger than our parent, to clip the top and bottom of the circle
Height = 1.3f;
- spinner = s;
+ Spinner = s;
Children = new Drawable[]
{
@@ -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,28 +115,28 @@ 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);
}
- if (!userTriggered && Time.Current >= spinner.EndTime)
+ if (!userTriggered && Time.Current >= Spinner.EndTime)
{
if (Progress >= 1)
AddJudgement(new OsuJudgement { Result = HitResult.Great });
@@ -143,7 +144,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
AddJudgement(new OsuJudgement { Result = HitResult.Good });
else if (Progress > .75)
AddJudgement(new OsuJudgement { Result = HitResult.Meh });
- else if (Time.Current >= spinner.EndTime)
+ else if (Time.Current >= Spinner.EndTime)
AddJudgement(new OsuJudgement { Result = HitResult.Miss });
}
}
@@ -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,36 +176,36 @@ 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);
+ float relativeCircleScale = Spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight;
+ 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()
{
base.UpdatePreemptState();
- circleContainer.ScaleTo(spinner.Scale * 0.3f);
- circleContainer.ScaleTo(spinner.Scale, TIME_PREEMPT / 1.4f, Easing.OutQuint);
+ 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
.ScaleTo(0)
- .ScaleTo(spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, TIME_PREEMPT - 150, Easing.OutQuint)
+ .ScaleTo(Spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, TIME_PREEMPT - 150, Easing.OutQuint)
.Then()
.ScaleTo(1, 500, Easing.OutQuint);
}
protected override void UpdateCurrentState(ArmedState state)
{
- var sequence = this.Delay(spinner.Duration).FadeOut(160);
+ var sequence = this.Delay(Spinner.Duration).FadeOut(160);
switch (state)
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
index 2068ad9205..46b4353f9a 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
@@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}
private readonly Slider slider;
- private readonly Box follow;
+ public readonly Box FollowCircle;
private readonly Box ball;
public SliderBall(Slider slider)
@@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Children = new Drawable[]
{
- follow = new Box
+ FollowCircle = new Box
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
@@ -101,11 +101,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
// If the current time is between the start and end of the slider, we should track mouse input regardless of the cursor position.
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => canCurrentlyTrack || base.ReceiveMouseInputAt(screenSpacePos);
- public override void ClearTransforms(bool propagateChildren = false, string targetMember = null)
+ public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null)
{
// Consider the case of rewinding - children's transforms are handled internally, so propagating down
// any further will cause weirdness with the Tracking bool below. Let's not propagate further at this point.
- base.ClearTransforms(false, targetMember);
+ base.ClearTransformsAfter(time, false, targetMember);
}
private bool tracking;
@@ -118,8 +118,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return;
tracking = value;
- follow.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint);
- follow.FadeTo(tracking ? 0.2f : 0, 300, Easing.OutQuint);
+ FollowCircle.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint);
+ FollowCircle.FadeTo(tracking ? 0.2f : 0, 300, Easing.OutQuint);
}
}
@@ -129,11 +129,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
base.Update();
- // Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position.
- Tracking = canCurrentlyTrack
- && lastState != null
- && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position)
- && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
+ if (Time.Current < slider.EndTime)
+ {
+ // Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position.
+ Tracking = canCurrentlyTrack
+ && lastState != null
+ && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position)
+ && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
+ }
}
public void UpdateProgress(double progress, int repeat)
@@ -141,4 +144,4 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Position = slider.Curve.PositionAt(progress);
}
}
-}
\ No newline at end of file
+}
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 5f9f11c783..ec51a10345 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -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 List(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..f307ff7c70
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs
@@ -0,0 +1,117 @@
+// 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 OpenTK.Graphics;
+using osu.Game.Rulesets.Osu.Judgements;
+using System.Collections.Generic;
+using System;
+using osu.Game.Rulesets.Mods;
+using System.Linq;
+using osu.Game.Rulesets.Scoring;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ [Ignore("getting CI working")]
+ public class TestCaseHitCircle : OsuTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DrawableHitCircle)
+ };
+
+ private readonly Container content;
+ protected override Container Content => content;
+
+ private int depthIndex;
+ protected readonly List Mods = new List();
+
+ public TestCaseHitCircle()
+ {
+ base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 }));
+
+ AddStep("Miss Big Single", () => testSingle(2));
+ AddStep("Miss Medium Single", () => testSingle(5));
+ AddStep("Miss Small Single", () => testSingle(7));
+ AddStep("Hit Big Single", () => testSingle(2, true));
+ AddStep("Hit Medium Single", () => testSingle(5, true));
+ AddStep("Hit Small Single", () => testSingle(7, true));
+ AddStep("Miss Big Stream", () => testStream(2));
+ AddStep("Miss Medium Stream", () => testStream(5));
+ AddStep("Miss Small Stream", () => testStream(7));
+ AddStep("Hit Big Stream", () => testStream(2, true));
+ AddStep("Hit Medium Stream", () => testStream(5, true));
+ AddStep("Hit Small Stream", () => testStream(7, true));
+ }
+
+ private void testSingle(float circleSize, bool auto = false, 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,
+ Depth = depthIndex++
+ };
+
+ foreach (var mod in Mods.OfType())
+ mod.ApplyToDrawableHitObjects(new[] { drawable });
+
+ Add(drawable);
+ }
+
+ private void testStream(float circleSize, bool auto = false)
+ {
+ Vector2 pos = new Vector2(-250, 0);
+
+ for (int i = 0; i <= 1000; i += 100)
+ {
+ testSingle(circleSize, auto, i, pos);
+ pos.X += 50;
+ }
+ }
+
+ 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)
+ {
+ // force success
+ AddJudgement(new OsuJudgement
+ {
+ Result = HitResult.Great
+ });
+ State.Value = ArmedState.Hit;
+ }
+ else
+ base.CheckForJudgements(userTriggered, timeOffset);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircleHidden.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircleHidden.cs
new file mode 100644
index 0000000000..7cc0c343a2
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircleHidden.cs
@@ -0,0 +1,22 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using osu.Game.Rulesets.Osu.Mods;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ [Ignore("getting CI working")]
+ public class TestCaseHitCircleHidden : TestCaseHitCircle
+ {
+ public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
+
+ public TestCaseHitCircleHidden()
+ {
+ Mods.Add(new OsuModHidden());
+ }
+ }
+}
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..1238572484
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs
@@ -0,0 +1,152 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using 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 OpenTK.Graphics;
+using osu.Game.Rulesets.Mods;
+using System.Linq;
+using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ [Ignore("getting CI working")]
+ public class TestCaseSlider : OsuTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(SliderBall),
+ typeof(SliderBody),
+ typeof(DrawableSlider),
+ typeof(DrawableRepeatPoint),
+ typeof(DrawableOsuHitObject)
+ };
+
+ private readonly Container content;
+ protected override Container Content => content;
+
+ private int depthIndex;
+ protected readonly List Mods = new List();
+
+ public TestCaseSlider()
+ {
+ base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 }));
+
+ AddStep("Big Single", () => testSimpleBig());
+ AddStep("Medium Single", () => testSimpleMedium());
+ AddStep("Small Single", () => testSimpleSmall());
+ AddStep("Big 1 Repeat", () => testSimpleBig(1));
+ AddStep("Medium 1 Repeat", () => testSimpleMedium(1));
+ AddStep("Small 1 Repeat", () => testSimpleSmall(1));
+ AddStep("Big 2 Repeats", () => testSimpleBig(2));
+ AddStep("Medium 2 Repeats", () => testSimpleMedium(2));
+ AddStep("Small 2 Repeats", () => testSimpleSmall(2));
+
+ AddStep("Slow Slider", testSlowSpeed); // slow long sliders take ages already so no repeat steps
+ AddStep("Slow Short Slider", () => testShortSlowSpeed());
+ AddStep("Slow Short Slider 1 Repeats", () => testShortSlowSpeed(1));
+ AddStep("Slow Short Slider 2 Repeats", () => testShortSlowSpeed(2));
+
+ AddStep("Fast Slider", () => testHighSpeed());
+ AddStep("Fast Slider 1 Repeat", () => testHighSpeed(1));
+ AddStep("Fast Slider 2 Repeats", () => testHighSpeed(2));
+ AddStep("Fast Short Slider", () => testShortHighSpeed());
+ AddStep("Fast Short Slider 1 Repeat", () => testShortHighSpeed(1));
+ AddStep("Fast Short Slider 2 Repeats", () => testShortHighSpeed(2));
+
+ AddStep("Perfect Curve", testCurve);
+ // TODO more curve types?
+ }
+
+ private void testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats);
+
+ private void testSimpleMedium(int repeats = 0) => createSlider(5, repeats: repeats);
+
+ private void testSimpleSmall(int repeats = 0) => createSlider(7, repeats: repeats);
+
+ private void testSlowSpeed() => createSlider(speedMultiplier: 0.5);
+
+ private void testShortSlowSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 0.5);
+
+ private void testHighSpeed(int repeats = 0) => createSlider(repeats: repeats, speedMultiplier: 15);
+
+ private void testShortHighSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 15);
+
+ private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2)
+ {
+ repeats++; // The first run through the slider is considered a repeat
+
+ var repeatSamples = new List>();
+ if (repeats > 1)
+ {
+ for (int i = 0; i < repeats; i++)
+ repeatSamples.Add(new List());
+ }
+
+ var slider = new Slider
+ {
+ StartTime = Time.Current + 1000,
+ Position = new Vector2(-(distance / 2), 0),
+ ComboColour = Color4.LightSeaGreen,
+ ControlPoints = new List
+ {
+ new Vector2(-(distance / 2), 0),
+ new Vector2(distance / 2, 0),
+ },
+ Distance = distance,
+ RepeatCount = repeats,
+ RepeatSamples = repeatSamples
+ };
+
+ addSlider(slider, circleSize, speedMultiplier);
+ }
+
+ private void testCurve()
+ {
+ 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(0, 200),
+ new Vector2(200, 0)
+ },
+ Distance = 600
+ };
+
+ addSlider(slider, 2, 3);
+ }
+
+ private void addSlider(Slider slider, float circleSize, double speedMultiplier)
+ {
+ var cpi = new ControlPointInfo();
+ cpi.DifficultyPoints.Add(new DifficultyControlPoint { SpeedMultiplier = speedMultiplier });
+
+ slider.ApplyDefaults(cpi, new BeatmapDifficulty { CircleSize = circleSize });
+
+ var drawable = new DrawableSlider(slider)
+ {
+ Anchor = Anchor.Centre,
+ Depth = depthIndex++
+ };
+
+ foreach (var mod in Mods.OfType())
+ mod.ApplyToDrawableHitObjects(new[] { drawable });
+
+ Add(drawable);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSliderHidden.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSliderHidden.cs
new file mode 100644
index 0000000000..016909ad73
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSliderHidden.cs
@@ -0,0 +1,22 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using osu.Game.Rulesets.Osu.Mods;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ [Ignore("getting CI working")]
+ public class TestCaseSliderHidden : TestCaseSlider
+ {
+ public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
+
+ public TestCaseSliderHidden()
+ {
+ Mods.Add(new OsuModHidden());
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs
new file mode 100644
index 0000000000..752574018c
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs
@@ -0,0 +1,88 @@
+// 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.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+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(SpinnerDisc),
+ typeof(DrawableSpinner),
+ typeof(DrawableOsuHitObject)
+ };
+
+ private readonly Container content;
+ protected override Container Content => content;
+
+ private int depthIndex;
+ protected readonly List Mods = new List();
+
+ public TestCaseSpinner()
+ {
+ base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 }));
+
+ AddStep("Miss Big", () => testSingle(2));
+ AddStep("Miss Medium", () => testSingle(5));
+ AddStep("Miss Small", () => testSingle(7));
+ AddStep("Hit Big", () => testSingle(2, true));
+ AddStep("Hit Medium", () => testSingle(5, true));
+ AddStep("Hit Small", () => testSingle(7, true));
+ }
+
+ private void testSingle(float circleSize, bool auto = false)
+ {
+ var spinner = new Spinner { StartTime = Time.Current + 1000, EndTime = Time.Current + 4000 };
+
+ spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize });
+
+ var drawable = new TestDrawableSpinner(spinner, auto)
+ {
+ Anchor = Anchor.Centre,
+ Depth = depthIndex++
+ };
+
+ foreach (var mod in Mods.OfType())
+ mod.ApplyToDrawableHitObjects(new[] { drawable });
+
+ Add(drawable);
+ }
+
+ private class TestDrawableSpinner : DrawableSpinner
+ {
+ private bool auto;
+
+ public TestDrawableSpinner(Spinner s, bool auto) : base(s)
+ {
+ this.auto = auto;
+ }
+
+ protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ {
+ if (auto && !userTriggered && Time.Current > Spinner.StartTime + Spinner.Duration / 2 && Progress < 1)
+ {
+ // force completion only once to not break human interaction
+ Disc.RotationAbsolute = Spinner.SpinsRequired * 360;
+ auto = false;
+ }
+
+ base.CheckForJudgements(userTriggered, timeOffset);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSpinnerHidden.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSpinnerHidden.cs
new file mode 100644
index 0000000000..9ef94b308f
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSpinnerHidden.cs
@@ -0,0 +1,22 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using osu.Game.Rulesets.Osu.Mods;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ [Ignore("getting CI working")]
+ public class TestCaseSpinnerHidden : TestCaseSpinner
+ {
+ public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
+
+ public TestCaseSpinnerHidden()
+ {
+ Mods.Add(new OsuModHidden());
+ }
+ }
+}
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.Taiko/Audio/DrumSampleMapping.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs
index 9d0037b97a..982b339d3a 100644
--- a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs
+++ b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
-using System.Linq;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Game.Audio;
@@ -13,7 +12,7 @@ namespace osu.Game.Rulesets.Taiko.Audio
public class DrumSampleMapping
{
private readonly ControlPointInfo controlPoints;
- private readonly Dictionary mappings = new Dictionary();
+ private readonly Dictionary mappings = new Dictionary();
public DrumSampleMapping(ControlPointInfo controlPoints, AudioManager audio)
{
@@ -26,17 +25,17 @@ namespace osu.Game.Rulesets.Taiko.Audio
else
samplePoints = controlPoints.SamplePoints;
- foreach (var s in samplePoints.Distinct())
+ foreach (var s in samplePoints)
{
- mappings[s] = new DrumSample
+ mappings[s.Time] = new DrumSample
{
- Centre = s.GetSampleInfo().GetChannel(audio.Sample),
- Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample)
+ 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)];
+ public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(time).Time];
public class DrumSample
{
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 fd35f0eaec..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;
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs
index cda82afe0e..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
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
index 5ca33aaea2..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
{
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
index 92da3fe09e..cc7dd2fa0f 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
@@ -41,6 +41,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
// 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 5a566fd091..a39d627cc4 100644
--- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
@@ -3,9 +3,6 @@
using osu.Game.Rulesets.Objects.Types;
using System;
-using System.Collections.Generic;
-using System.Linq;
-using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
@@ -75,13 +72,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
FirstTick = first,
TickSpacing = tickSpacing,
StartTime = t,
- IsStrong = IsStrong,
- Samples = new List(Samples.Select(s => new SampleInfo
- {
- Bank = s.Bank,
- Name = @"slidertick",
- Volume = s.Volume
- }))
+ IsStrong = IsStrong
});
first = false;
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 b1e6e9c4ce..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
{
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 bf1274256b..9b2ea095d2 100644
--- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
+++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
@@ -152,14 +152,14 @@ namespace osu.Game.Rulesets.Taiko.UI
target = centreHit;
back = centre;
- drumSample.Centre.Play();
+ drumSample.Centre?.Play();
}
else if (action == RimAction)
{
target = rimHit;
back = rim;
- drumSample.Rim.Play();
+ drumSample.Rim?.Play();
}
if (target != null)
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
index 839932c640..a0904ee446 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
@@ -70,7 +70,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition);
Assert.IsTrue(sprite.IsDrawable);
Assert.AreEqual(Anchor.Centre, sprite.Origin);
- Assert.AreEqual("SB/lyric/ja-21.png", sprite.Path);
+ Assert.AreEqual(Path.Combine("SB", "lyric", "ja-21.png"), sprite.Path);
var animation = background.Elements.ElementAt(12) as StoryboardAnimation;
Assert.NotNull(animation);
@@ -82,7 +82,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsTrue(animation.IsDrawable);
Assert.AreEqual(AnimationLoopType.LoopForever, animation.LoopType);
Assert.AreEqual(Anchor.Centre, animation.Origin);
- Assert.AreEqual("SB/red jitter/red_0000.jpg", animation.Path);
+ Assert.AreEqual(Path.Combine("SB", "red jitter", "red_0000.jpg"), animation.Path);
Assert.AreEqual(78993, animation.StartTime);
}
}
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/TestCaseMods.cs b/osu.Game.Tests/Visual/TestCaseMods.cs
index e535da3fcc..c78d3b1f9f 100644
--- a/osu.Game.Tests/Visual/TestCaseMods.cs
+++ b/osu.Game.Tests/Visual/TestCaseMods.cs
@@ -15,6 +15,8 @@ using System.Collections.Generic;
using osu.Game.Rulesets.Osu;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
+using osu.Game.Rulesets.Mania;
+using osu.Game.Rulesets.Mania.Mods;
using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
@@ -68,6 +70,9 @@ namespace osu.Game.Tests.Visual
case OsuRuleset or:
testOsuMods(or);
break;
+ case ManiaRuleset mr:
+ testManiaMods(mr);
+ break;
}
}
}
@@ -80,16 +85,27 @@ namespace osu.Game.Tests.Visual
var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail);
var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden);
+
var doubleTimeMod = harderMods.OfType().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime));
+
var autoPilotMod = assistMods.FirstOrDefault(m => m is OsuModAutopilot);
+ var easy = easierMods.FirstOrDefault(m => m is OsuModEasy);
+ var hardRock = harderMods.FirstOrDefault(m => m is OsuModHardRock);
+
testSingleMod(noFailMod);
testMultiMod(doubleTimeMod);
- testIncompatibleMods(noFailMod, autoPilotMod);
+ testIncompatibleMods(easy, hardRock);
testDeselectAll(easierMods.Where(m => !(m is MultiMod)));
testMultiplierTextColour(noFailMod, modSelect.LowMultiplierColour);
testMultiplierTextColour(hiddenMod, modSelect.HighMultiplierColour);
- testMultiplierTextUnranked(autoPilotMod);
+
+ testUnimplmentedMod(autoPilotMod);
+ }
+
+ private void testManiaMods(ManiaRuleset ruleset)
+ {
+ testMultiplierTextUnranked(ruleset.GetModsFor(ModType.Special).First(m => m is ManiaModRandom));
}
private void testSingleMod(Mod mod)
@@ -124,6 +140,12 @@ namespace osu.Game.Tests.Visual
checkNotSelected(mod);
}
+ private void testUnimplmentedMod(Mod mod)
+ {
+ selectNext(mod);
+ checkNotSelected(mod);
+ }
+
private void testIncompatibleMods(Mod modA, Mod modB)
{
selectNext(modA);
@@ -169,9 +191,9 @@ namespace osu.Game.Tests.Visual
AddAssert("check for ranked", () => !modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix));
}
- private void selectNext(Mod mod) => AddStep($"left click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext());
+ private void selectNext(Mod mod) => AddStep($"left click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(1));
- private void selectPrevious(Mod mod) => AddStep($"right click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectPrevious());
+ private void selectPrevious(Mod mod) => AddStep($"right click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(-1));
private void checkSelected(Mod mod)
{
diff --git a/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs b/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs
index a83cead213..46deca073f 100644
--- a/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
using osu.Game.Overlays;
@@ -19,11 +20,12 @@ namespace osu.Game.Tests.Visual
public override IReadOnlyList RequiredTypes => new[]
{
- typeof(Notification),
+ typeof(NotificationSection),
+ typeof(SimpleNotification),
typeof(ProgressNotification),
typeof(ProgressCompletionNotification),
- typeof(SimpleNotification),
typeof(IHasCompletionTarget),
+ typeof(Notification)
};
public TestCaseNotificationOverlay()
@@ -40,17 +42,44 @@ namespace osu.Game.Tests.Visual
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}"; };
- AddStep(@"toggle", manager.ToggleVisibility);
+
+ setState(Visibility.Visible);
AddStep(@"simple #1", sendHelloNotification);
AddStep(@"simple #2", sendAmazingNotification);
AddStep(@"progress #1", sendUploadProgress);
AddStep(@"progress #2", sendDownloadProgress);
- AddStep(@"barrage", () => sendBarrage());
+
+ 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))
{
@@ -80,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;
}
@@ -88,7 +117,7 @@ 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;
}
@@ -125,5 +154,11 @@ namespace osu.Game.Tests.Visual
{
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 18e40db064..3be4a18ec5 100644
--- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
@@ -51,11 +51,12 @@ namespace osu.Game.Tests.Visual
private class TestSongSelect : PlaySongSelect
{
public WorkingBeatmap CurrentBeatmap => Beatmap.Value;
+ public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap;
public new BeatmapCarousel Carousel => base.Carousel;
}
[BackgroundDependencyLoader]
- private void load(BeatmapManager baseManager)
+ private void load(OsuGameBase game)
{
TestSongSelect songSelect = null;
@@ -69,12 +70,16 @@ namespace osu.Game.Tests.Visual
dependencies.Cache(rulesets = new RulesetStore(contextFactory));
dependencies.Cache(manager = new BeatmapManager(storage, contextFactory, rulesets, null)
{
- DefaultBeatmap = defaultBeatmap = baseManager.GetWorkingBeatmap(null)
+ DefaultBeatmap = defaultBeatmap = game.Beatmap.Default
});
void loadNewSongSelect(bool deleteMaps = false) => AddStep("reload song select", () =>
{
- if (deleteMaps) manager.DeleteAll();
+ if (deleteMaps)
+ {
+ manager.DeleteAll();
+ game.Beatmap.SetDefault();
+ }
if (songSelect != null)
{
@@ -91,6 +96,8 @@ namespace osu.Game.Tests.Visual
AddAssert("dummy selected", () => songSelect.CurrentBeatmap == defaultBeatmap);
+ AddAssert("dummy shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap == defaultBeatmap);
+
AddStep("import test maps", () =>
{
for (int i = 0; i < 100; i += 10)
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/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/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index f586e0862d..ea7c4bd954 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -18,6 +18,7 @@ using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO;
using osu.Game.Database;
using osu.Game.Graphics;
+using osu.Game.Graphics.Textures;
using osu.Game.IO;
using osu.Game.IPC;
using osu.Game.Online.API;
@@ -101,15 +102,26 @@ namespace osu.Game.Beatmaps
///
public Func GetStableStorage { private get; set; }
+ private void refreshImportContext()
+ {
+ lock (importContextLock)
+ {
+ importContext?.Value?.Dispose();
+
+ importContext = new Lazy(() =>
+ {
+ var c = createContext();
+ c.Database.AutoTransactionsEnabled = false;
+ return c;
+ });
+ }
+ }
+
public BeatmapManager(Storage storage, Func context, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null)
{
createContext = context;
- importContext = new Lazy(() =>
- {
- var c = createContext();
- c.Database.AutoTransactionsEnabled = false;
- return c;
- });
+
+ refreshImportContext();
beatmaps = createBeatmapStore(context);
files = new FileStore(context, storage);
@@ -174,13 +186,16 @@ namespace osu.Game.Beatmaps
{
e = e.InnerException ?? e;
Logger.Error(e, $@"Could not import beatmap set ({Path.GetFileName(path)})");
+ refreshImportContext();
}
}
notification.State = ProgressNotificationState.Completed;
}
- private readonly Lazy importContext;
+ private readonly object importContextLock = new object();
+
+ private Lazy importContext;
///
/// Import a beatmap from an .
@@ -189,7 +204,7 @@ namespace osu.Game.Beatmaps
public BeatmapSetInfo Import(ArchiveReader archiveReader)
{
// let's only allow one concurrent import at a time for now.
- lock (importContext)
+ lock (importContextLock)
{
var context = importContext.Value;
@@ -314,7 +329,7 @@ namespace osu.Game.Beatmaps
/// The beatmap set to delete.
public void Delete(BeatmapSetInfo beatmapSet)
{
- lock (importContext)
+ lock (importContextLock)
{
var context = importContext.Value;
@@ -378,7 +393,7 @@ namespace osu.Game.Beatmaps
if (beatmapSet.Protected)
return;
- lock (importContext)
+ lock (importContextLock)
{
var context = importContext.Value;
@@ -652,7 +667,7 @@ namespace osu.Game.Beatmaps
try
{
- return new TextureStore(new RawTextureLoaderStore(store), false).Get(getPathForFile(Metadata.BackgroundFile));
+ return new LargeTextureStore(new RawTextureLoaderStore(store)).Get(getPathForFile(Metadata.BackgroundFile));
}
catch
{
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/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
index 8da6a0cefb..168f37e44e 100644
--- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
@@ -266,6 +266,6 @@ namespace osu.Game.Beatmaps.Formats
throw new InvalidDataException($@"Unknown origin: {value}");
}
- private string cleanFilename(string path) => FileSafety.PathStandardise(path.Trim('\"'));
+ private string cleanFilename(string path) => FileSafety.PathSanitise(path.Trim('\"'));
}
}
diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs
index 8eb2ddc0ab..4fb08a41f3 100644
--- a/osu.Game/Graphics/Backgrounds/Background.cs
+++ b/osu.Game/Graphics/Backgrounds/Background.cs
@@ -5,8 +5,8 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Textures;
using OpenTK.Graphics;
+using osu.Game.Graphics.Textures;
namespace osu.Game.Graphics.Backgrounds
{
@@ -22,7 +22,6 @@ namespace osu.Game.Graphics.Backgrounds
this.textureName = textureName;
RelativeSizeAxes = Axes.Both;
- Depth = float.MaxValue;
Add(Sprite = new Sprite
{
@@ -35,7 +34,7 @@ namespace osu.Game.Graphics.Backgrounds
}
[BackgroundDependencyLoader]
- private void load(TextureStore textures)
+ private void load(LargeTextureStore textures)
{
if (!string.IsNullOrEmpty(textureName))
Sprite.Texture = textures.Get(textureName);
diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
index c788df3066..f67da52fc0 100644
--- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
@@ -5,6 +5,8 @@ using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Input;
+using OpenTK;
namespace osu.Game.Graphics.Containers
{
@@ -22,6 +24,48 @@ namespace osu.Game.Graphics.Containers
StateChanged += onStateChanged;
}
+ ///
+ /// Whether mouse input should be blocked screen-wide while this overlay is visible.
+ /// Performing mouse actions outside of the valid extents will hide the overlay but pass the events through.
+ ///
+ public virtual bool BlockScreenWideMouse => BlockPassThroughMouse;
+
+ // receive input outside our bounds so we can trigger a close event on ourselves.
+ public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceiveMouseInputAt(screenSpacePos);
+
+ protected override bool OnWheel(InputState state)
+ {
+ // always allow wheel to pass through to stuff outside our DrawRectangle.
+ if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position))
+ return false;
+
+ return BlockPassThroughMouse;
+ }
+
+ protected override bool OnClick(InputState state)
+ {
+ if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position))
+ {
+ State = Visibility.Hidden;
+ return true;
+ }
+
+ return base.OnClick(state);
+ }
+
+ protected override bool OnDragStart(InputState state)
+ {
+ if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position))
+ {
+ State = Visibility.Hidden;
+ return true;
+ }
+
+ return base.OnDragStart(state);
+ }
+
+ protected override bool OnDrag(InputState state) => State == Visibility.Hidden;
+
private void onStateChanged(Visibility visibility)
{
switch (visibility)
diff --git a/osu.Game/Graphics/Textures/LargeTextureStore.cs b/osu.Game/Graphics/Textures/LargeTextureStore.cs
new file mode 100644
index 0000000000..166364c8dd
--- /dev/null
+++ b/osu.Game/Graphics/Textures/LargeTextureStore.cs
@@ -0,0 +1,18 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics.Textures;
+using osu.Framework.IO.Stores;
+
+namespace osu.Game.Graphics.Textures
+{
+ ///
+ /// A texture store that bypasses atlasing.
+ ///
+ public class LargeTextureStore : TextureStore
+ {
+ public LargeTextureStore(IResourceStore store = null) : base(store, false)
+ {
+ }
+ }
+}
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 1dc4c487f3..257b78ea0a 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -27,6 +27,7 @@ using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
using osu.Game.Screens.Play;
using osu.Game.Input.Bindings;
+using OpenTK.Graphics;
namespace osu.Game
{
@@ -65,6 +66,8 @@ namespace osu.Game
public float ToolbarOffset => Toolbar.Position.Y + Toolbar.DrawHeight;
+ public readonly BindableBool ShowOverlays = new BindableBool();
+
private OsuScreen screenStack;
private VolumeControl volume;
@@ -280,6 +283,21 @@ namespace osu.Game
settings.StateChanged += _ => updateScreenOffset();
notifications.StateChanged += _ => updateScreenOffset();
+ notifications.Enabled.BindTo(ShowOverlays);
+
+ ShowOverlays.ValueChanged += show =>
+ {
+ //central game screen change logic.
+ if (!show)
+ {
+ hideAllOverlays();
+ musicController.State = Visibility.Hidden;
+ Toolbar.State = Visibility.Hidden;
+ }
+ else
+ Toolbar.State = Visibility.Visible;
+ };
+
Cursor.State = Visibility.Hidden;
}
@@ -314,10 +332,21 @@ namespace osu.Game
}
private Task asyncLoadStream;
+ private int visibleOverlayCount;
private void loadComponentSingleFile(T d, Action add)
where T : Drawable
{
+ var focused = d as FocusedOverlayContainer;
+ if (focused != null)
+ {
+ focused.StateChanged += s =>
+ {
+ visibleOverlayCount += s == Visibility.Visible ? 1 : -1;
+ screenStack.FadeColour(visibleOverlayCount > 0 ? OsuColour.Gray(0.5f) : Color4.White, 500, Easing.OutQuint);
+ };
+ }
+
// schedule is here to ensure that all component loads are done after LoadComplete is run (and thus all dependencies are cached).
// with some better organisation of LoadComplete to do construction and dependency caching in one step, followed by calls to loadComponentSingleFile,
// we could avoid the need for scheduling altogether.
@@ -361,8 +390,6 @@ namespace osu.Game
public bool OnReleased(GlobalAction action) => false;
- public event Action ScreenChanged;
-
private Container mainContent;
private Container overlayContent;
@@ -380,29 +407,6 @@ namespace osu.Game
notifications.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);
- }
-
protected override bool OnExiting()
{
if (screenStack.ChildScreen == null) return false;
@@ -448,15 +452,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 ea0bf22112..e311aea8e4 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -18,8 +18,10 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Online.API;
using osu.Framework.Graphics.Performance;
+using osu.Framework.Graphics.Textures;
using osu.Framework.Logging;
using osu.Game.Database;
+using osu.Game.Graphics.Textures;
using osu.Game.Input;
using osu.Game.Input.Bindings;
using osu.Game.IO;
@@ -89,6 +91,8 @@ namespace osu.Game
{
dependencies.Cache(contextFactory = new DatabaseContextFactory(Host));
+ dependencies.Cache(new LargeTextureStore(new RawTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures"))));
+
dependencies.Cache(this);
dependencies.Cache(LocalConfig);
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/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs
index dcabad8ed6..20c1cf9332 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -241,8 +241,6 @@ namespace osu.Game.Overlays
public override bool AcceptsFocus => true;
- protected override bool OnClick(InputState state) => true;
-
protected override void OnFocus(InputState state)
{
//this is necessary as textbox is masked away and therefore can't get focus :(
diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs
index 77b7c3add2..21f07bb0a4 100644
--- a/osu.Game/Overlays/Mods/ModButton.cs
+++ b/osu.Game/Overlays/Mods/ModButton.cs
@@ -32,7 +32,10 @@ namespace osu.Game.Overlays.Mods
private readonly Container iconsContainer;
private SampleChannel sampleOn, sampleOff;
- public Action Action; // Passed the selected mod or null if none
+ ///
+ /// Fired when the selection changes.
+ ///
+ public Action SelectionChanged;
public string TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty;
@@ -42,71 +45,73 @@ namespace osu.Game.Overlays.Mods
// A selected index of -1 means not selected.
private int selectedIndex = -1;
- protected int SelectedIndex
+ ///
+ /// Change the selected mod index of this button.
+ ///
+ /// The new index.
+ /// Whether the selection changed.
+ private bool changeSelectedIndex(int newIndex)
{
- get
+ if (newIndex == selectedIndex) return false;
+
+ int direction = newIndex < selectedIndex ? -1 : 1;
+ bool beforeSelected = Selected;
+
+ Mod modBefore = SelectedMod ?? Mods[0];
+
+ if (newIndex >= Mods.Length)
+ newIndex = -1;
+ else if (newIndex < -1)
+ newIndex = Mods.Length - 1;
+
+ if (newIndex >= 0 && !Mods[newIndex].HasImplementation)
+ return false;
+
+ selectedIndex = newIndex;
+ Mod modAfter = SelectedMod ?? Mods[0];
+
+ if (beforeSelected != Selected)
{
- return selectedIndex;
+ iconsContainer.RotateTo(Selected ? 5f : 0f, 300, Easing.OutElastic);
+ iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, Easing.OutElastic);
}
- set
+
+ if (modBefore != modAfter)
{
- if (value == selectedIndex) return;
+ const float rotate_angle = 16;
- int direction = value < selectedIndex ? -1 : 1;
- bool beforeSelected = Selected;
+ foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing);
+ backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing);
- Mod modBefore = SelectedMod ?? Mods[0];
-
- if (value >= Mods.Length)
- selectedIndex = -1;
- else if (value < -1)
- selectedIndex = Mods.Length - 1;
- else
- selectedIndex = value;
-
- Mod modAfter = SelectedMod ?? Mods[0];
-
- if (beforeSelected != Selected)
+ backgroundIcon.Icon = modAfter.Icon;
+ using (BeginDelayedSequence(mod_switch_duration, true))
{
- iconsContainer.RotateTo(Selected ? 5f : 0f, 300, Easing.OutElastic);
- iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, Easing.OutElastic);
+ foregroundIcon
+ .RotateTo(-rotate_angle * direction)
+ .RotateTo(0f, mod_switch_duration, mod_switch_easing);
+
+ backgroundIcon
+ .RotateTo(rotate_angle * direction)
+ .RotateTo(0f, mod_switch_duration, mod_switch_easing);
+
+ Schedule(() => displayMod(modAfter));
}
-
- if (modBefore != modAfter)
- {
- const float rotate_angle = 16;
-
- foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing);
- backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing);
-
- backgroundIcon.Icon = modAfter.Icon;
- using (BeginDelayedSequence(mod_switch_duration, true))
- {
- foregroundIcon
- .RotateTo(-rotate_angle * direction)
- .RotateTo(0f, mod_switch_duration, mod_switch_easing);
-
- backgroundIcon
- .RotateTo(rotate_angle * direction)
- .RotateTo(0f, mod_switch_duration, mod_switch_easing);
-
- Schedule(() => displayMod(modAfter));
- }
- }
-
- foregroundIcon.Highlighted = Selected;
}
+
+ foregroundIcon.Highlighted = Selected;
+
+ (selectedIndex == -1 ? sampleOff : sampleOn).Play();
+ SelectionChanged?.Invoke(SelectedMod);
+ return true;
}
- public bool Selected => SelectedIndex != -1;
+ public bool Selected => selectedIndex != -1;
private Color4 selectedColour;
+
public Color4 SelectedColour
{
- get
- {
- return selectedColour;
- }
+ get { return selectedColour; }
set
{
if (value == selectedColour) return;
@@ -116,12 +121,10 @@ namespace osu.Game.Overlays.Mods
}
private Mod mod;
+
public Mod Mod
{
- get
- {
- return mod;
- }
+ get { return mod; }
set
{
mod = value;
@@ -147,9 +150,7 @@ namespace osu.Game.Overlays.Mods
public Mod[] Mods { get; private set; }
- // the mods from Mod, only multiple if Mod is a MultiMod
-
- public virtual Mod SelectedMod => Mods.ElementAtOrDefault(SelectedIndex);
+ public virtual Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex);
[BackgroundDependencyLoader]
private void load(AudioManager audio)
@@ -163,31 +164,42 @@ namespace osu.Game.Overlays.Mods
switch (args.Button)
{
case MouseButton.Left:
- SelectNext();
+ SelectNext(1);
break;
case MouseButton.Right:
- SelectPrevious();
+ SelectNext(-1);
break;
}
+
return true;
}
- public void SelectNext()
+ ///
+ /// Select the next available mod in a specified direction.
+ ///
+ /// 1 for forwards, -1 for backwards.
+ public void SelectNext(int direction)
{
- (++SelectedIndex == Mods.Length ? sampleOff : sampleOn).Play();
- Action?.Invoke(SelectedMod);
+ int start = selectedIndex + direction;
+ // wrap around if we are at an extremity.
+ if (start >= Mods.Length)
+ start = -1;
+ else if (start < -1)
+ start = Mods.Length - 1;
+
+ for (int i = start; i < Mods.Length && i >= 0; i += direction)
+ {
+ if (Mods[i].HasImplementation)
+ {
+ changeSelectedIndex(i);
+ return;
+ }
+ }
+
+ Deselect();
}
- public void SelectPrevious()
- {
- (--SelectedIndex == -1 ? sampleOff : sampleOn).Play();
- Action?.Invoke(SelectedMod);
- }
-
- public void Deselect()
- {
- SelectedIndex = -1;
- }
+ public void Deselect() => changeSelectedIndex(-1);
private void displayMod(Mod mod)
{
@@ -195,6 +207,7 @@ namespace osu.Game.Overlays.Mods
backgroundIcon.Icon = foregroundIcon.Icon;
foregroundIcon.Icon = mod.Icon;
text.Text = mod.Name;
+ Colour = mod.HasImplementation ? Color4.White : Color4.Gray;
}
private void createIcons()
@@ -204,13 +217,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 +233,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 +272,15 @@ 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/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs
index aac8a72dd7..50310d1b27 100644
--- a/osu.Game/Overlays/Mods/ModSection.cs
+++ b/osu.Game/Overlays/Mods/ModSection.cs
@@ -51,7 +51,7 @@ namespace osu.Game.Overlays.Mods
return new ModButton(m)
{
SelectedColour = selectedColour,
- Action = Action,
+ SelectionChanged = Action,
};
}).ToArray();
@@ -83,26 +83,33 @@ namespace osu.Game.Overlays.Mods
{
var index = Array.IndexOf(ToggleKeys, args.Key);
if (index > -1 && index < buttons.Length)
- buttons[index].SelectNext();
+ buttons[index].SelectNext(state.Keyboard.ShiftPressed ? -1 : 1);
return base.OnKeyDown(state, args);
}
- public void DeselectAll()
- {
- foreach (ModButton button in buttons)
- button.Deselect();
- }
+ public void DeselectAll() => DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null));
- public void DeselectTypes(Type[] modTypes)
+ ///
+ /// Deselect one or more mods in this section.
+ ///
+ /// The types of s which should be deselected.
+ /// Set to true to bypass animations and update selections immediately.
+ public void DeselectTypes(IEnumerable modTypes, bool immediate = false)
{
+ int delay = 0;
foreach (var button in buttons)
{
Mod selected = button.SelectedMod;
if (selected == null) continue;
foreach (Type type in modTypes)
if (type.IsInstanceOfType(selected))
- button.Deselect();
+ {
+ if (immediate)
+ button.Deselect();
+ else
+ Scheduler.AddDelayed(() => button.Deselect(), delay += 50);
+ }
}
}
diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
index 9639907914..cc5a17358d 100644
--- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs
+++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
@@ -100,17 +100,22 @@ namespace osu.Game.Overlays.Mods
refreshSelectedMods();
}
- public void DeselectTypes(Type[] modTypes)
+ ///
+ /// Deselect one or more mods.
+ ///
+ /// The types of s which should be deselected.
+ /// Set to true to bypass animations and update selections immediately.
+ public void DeselectTypes(Type[] modTypes, bool immediate = false)
{
if (modTypes.Length == 0) return;
foreach (ModSection section in ModSectionsContainer.Children)
- section.DeselectTypes(modTypes);
+ section.DeselectTypes(modTypes, immediate);
}
private void modButtonPressed(Mod selectedMod)
{
if (selectedMod != null)
- DeselectTypes(selectedMod.IncompatibleMods);
+ DeselectTypes(selectedMod.IncompatibleMods, true);
refreshSelectedMods();
}
@@ -127,10 +132,6 @@ namespace osu.Game.Overlays.Mods
ranked &= mod.Ranked;
}
- // 1.00x
- // 1.05x
- // 1.20x
-
MultiplierLabel.Text = $"{multiplier:N2}x";
if (!ranked)
MultiplierLabel.Text += " (Unranked)";
diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs
index d7c553aa7d..685fb5c6ab 100644
--- a/osu.Game/Overlays/MusicController.cs
+++ b/osu.Game/Overlays/MusicController.cs
@@ -65,10 +65,10 @@ namespace osu.Game.Overlays
AlwaysPresent = true;
}
- protected override bool OnDragStart(InputState state) => true;
-
protected override bool OnDrag(InputState state)
{
+ if (base.OnDrag(state)) return true;
+
Trace.Assert(state.Mouse.PositionMouseDown != null, "state.Mouse.PositionMouseDown != null");
// ReSharper disable once PossibleInvalidOperationException
@@ -78,7 +78,7 @@ namespace osu.Game.Overlays
change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length;
dragContainer.MoveTo(change);
- return base.OnDrag(state);
+ return true;
}
protected override bool OnDragEnd(InputState state)
diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs
index 2f1c3285ef..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;
@@ -11,7 +10,9 @@ 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
{
@@ -21,6 +22,11 @@ namespace osu.Game.Overlays
public const float TRANSITION_LENGTH = 600;
+ ///
+ /// Whether posted notifications should be processed.
+ ///
+ public readonly BindableBool Enabled = new BindableBool(true);
+
private FlowContainer sections;
///
@@ -28,6 +34,27 @@ namespace osu.Game.Overlays
///
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()
{
@@ -85,14 +112,21 @@ namespace osu.Game.Overlays
private void notificationClosed()
{
- // hide ourselves if all notifications have been dismissed.
- if (totalCount == 0)
- State = Visibility.Hidden;
+ Schedule(() =>
+ {
+ // hide ourselves if all notifications have been dismissed.
+ if (totalCount == 0)
+ State = Visibility.Hidden;
+ });
updateCounts();
}
- public void Post(Notification notification) => Schedule(() =>
+ private readonly Scheduler postScheduler = new Scheduler();
+
+ private bool processingPosts = true;
+
+ public void Post(Notification notification) => postScheduler.Add(() =>
{
++runningDepth;
notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth;
@@ -109,6 +143,13 @@ namespace osu.Game.Overlays
updateCounts();
});
+ protected override void Update()
+ {
+ base.Update();
+ if (processingPosts)
+ postScheduler.Update();
+ }
+
protected override void PopIn()
{
base.PopIn();
diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs
index 2bd0321d12..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;
@@ -33,6 +33,7 @@ namespace osu.Game.Overlays.Notifications
public IEnumerable AcceptTypes;
private string clearText;
+
public string ClearText
{
get { return clearText; }
@@ -110,7 +111,7 @@ namespace osu.Game.Overlays.Notifications
},
},
},
- notifications = new FillFlowContainer
+ notifications = new AlwaysUpdateFillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
@@ -159,4 +160,13 @@ namespace osu.Game.Overlays.Notifications
notifications?.Children.ForEach(n => n.Read = true);
}
}
+
+ 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 c39c630858..d797372390 100644
--- a/osu.Game/Overlays/Notifications/ProgressNotification.cs
+++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs
@@ -95,8 +95,8 @@ namespace osu.Game.Overlays.Notifications
protected virtual void Completed()
{
- base.Close();
CompletionTarget?.Invoke(CreateCompletionNotification());
+ base.Close();
}
public override bool DisplayOnTop => false;
diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs
index 18e77cf186..960bb60287 100644
--- a/osu.Game/Overlays/Profile/ProfileHeader.cs
+++ b/osu.Game/Overlays/Profile/ProfileHeader.cs
@@ -109,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,
@@ -333,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();
diff --git a/osu.Game/Overlays/Profile/SupporterIcon.cs b/osu.Game/Overlays/Profile/SupporterIcon.cs
index 570d5a13bb..b5cd94e54b 100644
--- a/osu.Game/Overlays/Profile/SupporterIcon.cs
+++ b/osu.Game/Overlays/Profile/SupporterIcon.cs
@@ -1,20 +1,23 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
+// 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
+ public class SupporterIcon : CircularContainer, IHasTooltip
{
private readonly Box background;
+ public string TooltipText => "osu!supporter";
+
public SupporterIcon()
{
Masking = true;
diff --git a/osu.Game/Overlays/Settings/Sections/AudioSection.cs b/osu.Game/Overlays/Settings/Sections/AudioSection.cs
index c994a6296c..738c9e94d7 100644
--- a/osu.Game/Overlays/Settings/Sections/AudioSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/AudioSection.cs
@@ -10,7 +10,7 @@ namespace osu.Game.Overlays.Settings.Sections
public class AudioSection : SettingsSection
{
public override string Header => "Audio";
- public override FontAwesome Icon => FontAwesome.fa_headphones;
+ public override FontAwesome Icon => FontAwesome.fa_volume_up;
public AudioSection()
{
@@ -23,4 +23,4 @@ namespace osu.Game.Overlays.Settings.Sections
};
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs
index a80f6d4da8..f798d63e5a 100644
--- a/osu.Game/Overlays/SettingsOverlay.cs
+++ b/osu.Game/Overlays/SettingsOverlay.cs
@@ -177,8 +177,6 @@ namespace osu.Game.Overlays
public override bool AcceptsFocus => true;
- protected override bool OnClick(InputState state) => true;
-
protected override void OnFocus(InputState state)
{
GetContainingInputManager().ChangeFocus(searchTextBox);
diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs
index e36db1f9da..ef1f7c8c70 100644
--- a/osu.Game/Overlays/Toolbar/Toolbar.cs
+++ b/osu.Game/Overlays/Toolbar/Toolbar.cs
@@ -62,10 +62,10 @@ namespace osu.Game.Overlays.Toolbar
new ToolbarChatButton(),
new ToolbarSocialButton(),
new ToolbarMusicButton(),
- new ToolbarButton
- {
- Icon = FontAwesome.fa_search
- },
+ //new ToolbarButton
+ //{
+ // Icon = FontAwesome.fa_search
+ //},
userArea = new ToolbarUserArea(),
new ToolbarNotificationButton(),
}
diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs
index 9aa660147a..c2e7fc5b44 100644
--- a/osu.Game/Overlays/UserProfileOverlay.cs
+++ b/osu.Game/Overlays/UserProfileOverlay.cs
@@ -10,7 +10,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
@@ -34,15 +33,6 @@ namespace osu.Game.Overlays
public const float CONTENT_X_MARGIN = 50;
- // receive input outside our bounds so we can trigger a close event on ourselves.
- public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
-
- protected override bool OnClick(InputState state)
- {
- State = Visibility.Hidden;
- return true;
- }
-
public UserProfileOverlay()
{
FirstWaveColour = OsuColour.Gray(0.4f);
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/IApplicableFailOverride.cs b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs
new file mode 100644
index 0000000000..2d7cda5f1f
--- /dev/null
+++ b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs
@@ -0,0 +1,16 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Rulesets.Mods
+{
+ ///
+ /// Represents a mod which can override (and block) a fail.
+ ///
+ public interface IApplicableFailOverride : IApplicableMod
+ {
+ ///
+ /// Whether we should allow failing at the current point in time.
+ ///
+ bool AllowFail { get; }
+ }
+}
diff --git a/osu.Game/Rulesets/Mods/IApplicableMod.cs b/osu.Game/Rulesets/Mods/IApplicableMod.cs
new file mode 100644
index 0000000000..ed2b652598
--- /dev/null
+++ b/osu.Game/Rulesets/Mods/IApplicableMod.cs
@@ -0,0 +1,13 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Rulesets.Mods
+{
+ ///
+ /// The base interface for a mod which can be applied in some way.
+ /// If this is not implemented by a mod, it will not be available for use in-game.
+ ///
+ public interface IApplicableMod
+ {
+ }
+}
diff --git a/osu.Game/Rulesets/Mods/IApplicableToClock.cs b/osu.Game/Rulesets/Mods/IApplicableToClock.cs
index f0502cf346..8bb4e2b97d 100644
--- a/osu.Game/Rulesets/Mods/IApplicableToClock.cs
+++ b/osu.Game/Rulesets/Mods/IApplicableToClock.cs
@@ -8,8 +8,8 @@ namespace osu.Game.Rulesets.Mods
///
/// An interface for mods that make adjustments to the track.
///
- public interface IApplicableToClock
+ public interface IApplicableToClock : IApplicableMod
{
void ApplyToClock(IAdjustableClock clock);
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs b/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs
index 58f5defb5e..a95aa4370c 100644
--- a/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs
+++ b/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs
@@ -8,8 +8,8 @@ namespace osu.Game.Rulesets.Mods
///
/// An interface for mods that make general adjustments to difficulty.
///
- public interface IApplicableToDifficulty
+ public interface IApplicableToDifficulty : IApplicableMod
{
void ApplyToDifficulty(BeatmapDifficulty difficulty);
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs b/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs
new file mode 100644
index 0000000000..66dbc85095
--- /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 : IApplicableMod
+ {
+ ///
+ /// Applies this to a list of s.
+ ///
+ /// The list of s to apply to.
+ void ApplyToDrawableHitObjects(IEnumerable drawables);
+ }
+}
diff --git a/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs b/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs
index 7f39def343..1964ad728f 100644
--- a/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs
+++ b/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs
@@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mods
///
/// An interface for s that can be applied to s.
///
- public interface IApplicableToHitObject
+ public interface IApplicableToHitObject : IApplicableMod
where TObject : HitObject
{
///
diff --git a/osu.Game/Rulesets/Mods/IApplicableToRulesetContainer.cs b/osu.Game/Rulesets/Mods/IApplicableToRulesetContainer.cs
index 9b23dd58f9..eae8c9d15c 100644
--- a/osu.Game/Rulesets/Mods/IApplicableToRulesetContainer.cs
+++ b/osu.Game/Rulesets/Mods/IApplicableToRulesetContainer.cs
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Mods
///
/// An interface for s that can be applied to s.
///
- public interface IApplicableToRulesetContainer
+ public interface IApplicableToRulesetContainer : IApplicableMod
where TObject : HitObject
{
///
diff --git a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs
index db9b713c59..2314999d4f 100644
--- a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs
+++ b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs
@@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mods
///
/// An interface for mods that make general adjustments to score processor.
///
- public interface IApplicableToScoreProcessor
+ public interface IApplicableToScoreProcessor : IApplicableMod
{
void ApplyToScoreProcessor(ScoreProcessor scoreProcessor);
}
diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs
index 7b0034863e..68ed545701 100644
--- a/osu.Game/Rulesets/Mods/Mod.cs
+++ b/osu.Game/Rulesets/Mods/Mod.cs
@@ -41,6 +41,11 @@ namespace osu.Game.Rulesets.Mods
///
public abstract double ScoreMultiplier { get; }
+ ///
+ /// Returns true if this mod is implemented (and playable).
+ ///
+ public virtual bool HasImplementation => this is IApplicableMod;
+
///
/// Returns if this mod is ranked.
///
@@ -50,10 +55,5 @@ namespace osu.Game.Rulesets.Mods
/// The mods this mod cannot be enabled with.
///
public virtual Type[] IncompatibleMods => new Type[] { };
-
- ///
- /// Whether we should allow failing at the current point in time.
- ///
- public virtual bool AllowFail => true;
}
}
diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs
index d94d4ba0db..bddb97f374 100644
--- a/osu.Game/Rulesets/Mods/ModAutoplay.cs
+++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mods
}
}
- public class ModAutoplay : Mod
+ public class ModAutoplay : Mod, IApplicableFailOverride
{
public override string Name => "Autoplay";
public override string ShortenedName => "AT";
@@ -29,5 +29,6 @@ namespace osu.Game.Rulesets.Mods
public override string Description => "Watch a perfect automated play through the song";
public override double ScoreMultiplier => 0;
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) };
+ public bool AllowFail => false;
}
}
diff --git a/osu.Game/Rulesets/Mods/ModNoFail.cs b/osu.Game/Rulesets/Mods/ModNoFail.cs
index 3a3878d77e..8aefd1b88e 100644
--- a/osu.Game/Rulesets/Mods/ModNoFail.cs
+++ b/osu.Game/Rulesets/Mods/ModNoFail.cs
@@ -6,7 +6,7 @@ using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModNoFail : Mod
+ public abstract class ModNoFail : Mod, IApplicableFailOverride
{
public override string Name => "NoFail";
public override string ShortenedName => "NF";
@@ -20,6 +20,6 @@ namespace osu.Game.Rulesets.Mods
///
/// We never fail, 'yo.
///
- public override bool AllowFail => false;
+ public bool AllowFail => false;
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index ab4c04be3f..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
{
@@ -74,6 +75,9 @@ namespace osu.Game.Rulesets.Objects.Drawables
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();
protected DrawableHitObject(TObject hitObject)
@@ -101,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;
@@ -119,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();
};
@@ -240,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/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 8d09b554dd..3706033b68 100644
--- a/osu.Game/Rulesets/UI/RulesetContainer.cs
+++ b/osu.Game/Rulesets/UI/RulesetContainer.cs
@@ -261,6 +261,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/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs
index de84e90baf..36867a84d5 100644
--- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs
+++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs
@@ -3,6 +3,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
+using osu.Framework.Threading;
using osu.Game.Graphics.Backgrounds;
namespace osu.Game.Screens.Backgrounds
@@ -24,16 +25,22 @@ namespace osu.Game.Screens.Backgrounds
private void display(Background newBackground)
{
- current?.FadeOut(800, Easing.OutQuint);
+ current?.FadeOut(800, Easing.InOutSine);
current?.Expire();
Add(current = newBackground);
+ currentDisplay++;
}
+ private ScheduledDelegate nextTask;
+
public void Next()
{
- currentDisplay++;
- LoadComponentAsync(new Background(backgroundName) { Depth = currentDisplay }, display);
+ nextTask?.Cancel();
+ nextTask = Scheduler.AddDelayed(() =>
+ {
+ LoadComponentAsync(new Background(backgroundName) { Depth = currentDisplay }, display);
+ }, 100);
}
}
}
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 2cbb203c37..d4cfa0b3d4 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;
@@ -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
@@ -291,7 +298,7 @@ namespace osu.Game.Screens.Play
private bool onFail()
{
- if (Beatmap.Value.Mods.Value.Any(m => !m.AllowFail))
+ if (Beatmap.Value.Mods.Value.OfType().Any(m => !m.AllowFail))
return false;
decoupledClock.Stop();
diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs
index a220dcee0e..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;
diff --git a/osu.Game/Screens/Ranking/ResultsPageScore.cs b/osu.Game/Screens/Ranking/ResultsPageScore.cs
index 25a42cae1c..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;
@@ -209,7 +210,7 @@ namespace osu.Game.Screens.Ranking
Origin = Anchor.TopCentre,
},
new OsuSpriteText {
- Text = statistic.Key,
+ 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,
}
};
diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs
index 9d7867659d..98acd0815d 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
@@ -76,7 +81,11 @@ namespace osu.Game.Screens.Select
itemsCache.Invalidate();
scrollPositionCache.Invalidate();
- Schedule(() => BeatmapSetsChanged?.Invoke());
+ Schedule(() =>
+ {
+ BeatmapSetsChanged?.Invoke();
+ initialLoadComplete = true;
+ });
}));
}
}
@@ -185,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.
@@ -213,11 +229,15 @@ namespace osu.Game.Screens.Select
}
}
- public void SelectNextRandom()
+ ///
+ /// Select the next beatmap in the random sequence.
+ ///
+ /// True if a selection could be made, else False.
+ public bool SelectNextRandom()
{
var visible = beatmapSets.Where(s => !s.Filtered).ToList();
if (!visible.Any())
- return;
+ return false;
if (selectedBeatmap != null)
{
@@ -247,6 +267,7 @@ namespace osu.Game.Screens.Select
set = visible.ElementAt(RNG.Next(visible.Count));
select(set.Beatmaps.Skip(RNG.Next(set.Beatmaps.Count())).FirstOrDefault());
+ return true;
}
public void SelectPreviousRandom()
@@ -513,7 +534,7 @@ namespace osu.Game.Screens.Select
currentY += DrawHeight / 2;
scrollableContent.Height = currentY;
- if (selectedBeatmapSet == null || selectedBeatmap == 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/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
index fa1326de8c..c25ba66475 100644
--- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs
+++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
@@ -20,6 +20,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Cursor;
+using osu.Framework.Localisation;
namespace osu.Game.Screens.Select
{
@@ -71,10 +72,6 @@ namespace osu.Game.Screens.Select
{
State = beatmap == null ? Visibility.Hidden : Visibility.Visible;
- // ensure we ourselves are visible if not already.
- if (!IsPresent)
- State = Visibility.Visible;
-
Info?.FadeOut(250);
Info?.Expire();
@@ -90,6 +87,8 @@ namespace osu.Game.Screens.Select
public OsuSpriteText ArtistLabel { get; private set; }
public FillFlowContainer MapperContainer { get; private set; }
public FillFlowContainer InfoLabelContainer { get; private set; }
+ private UnicodeBindableString titleBinding;
+ private UnicodeBindableString artistBinding;
public BufferedWedgeInfo(WorkingBeatmap working)
{
@@ -97,7 +96,7 @@ namespace osu.Game.Screens.Select
}
[BackgroundDependencyLoader]
- private void load()
+ private void load(LocalisationEngine localisation)
{
var beatmapInfo = working.BeatmapInfo;
var metadata = beatmapInfo.Metadata ?? working.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
@@ -106,6 +105,9 @@ namespace osu.Game.Screens.Select
CacheDrawnFrameBuffer = true;
RelativeSizeAxes = Axes.Both;
+ titleBinding = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title);
+ artistBinding = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist);
+
Children = new Drawable[]
{
// We will create the white-to-black gradient by modulating transparency and having
@@ -171,13 +173,11 @@ namespace osu.Game.Screens.Select
TitleLabel = new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
- Text = string.IsNullOrEmpty(metadata.Source) ? metadata.Title : metadata.Source + " — " + metadata.Title,
TextSize = 28,
},
ArtistLabel = new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
- Text = metadata.Artist,
TextSize = 17,
},
MapperContainer = new FillFlowContainer
@@ -197,6 +197,15 @@ namespace osu.Game.Screens.Select
}
}
};
+ artistBinding.ValueChanged += value => setMetadata(metadata.Source);
+ artistBinding.TriggerChange();
+ }
+
+ private void setMetadata(string source)
+ {
+ ArtistLabel.Text = artistBinding.Value;
+ TitleLabel.Text = string.IsNullOrEmpty(source) ? titleBinding.Value : source + " — " + titleBinding.Value;
+ ForceRedraw();
}
private InfoLabel[] getInfoLabels()
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/ImportFromStablePopup.cs b/osu.Game/Screens/Select/ImportFromStablePopup.cs
index 489ab79fa4..03e9462636 100644
--- a/osu.Game/Screens/Select/ImportFromStablePopup.cs
+++ b/osu.Game/Screens/Select/ImportFromStablePopup.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Screens.Select
HeaderText = @"You have no beatmaps!";
BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps?";
- Icon = FontAwesome.fa_trash_o;
+ Icon = FontAwesome.fa_plane;
Buttons = new PopupDialogButton[]
{
diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
index c15a179e8c..ac47fade48 100644
--- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
+++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
@@ -109,6 +109,13 @@ namespace osu.Game.Screens.Select.Leaderboards
{
if (value == placeholderState) return;
+ if (value != PlaceholderState.Successful)
+ {
+ getScoresRequest?.Cancel();
+ getScoresRequest = null;
+ Scores = null;
+ }
+
switch (placeholderState = value)
{
case PlaceholderState.NetworkFailure:
@@ -211,10 +218,6 @@ namespace osu.Game.Screens.Select.Leaderboards
private void updateScores()
{
- getScoresRequest?.Cancel();
- getScoresRequest = null;
- Scores = null;
-
if (Scope == LeaderboardScope.Local)
{
// TODO: get local scores from wherever here.
@@ -234,16 +237,15 @@ namespace osu.Game.Screens.Select.Leaderboards
return;
}
- PlaceholderState = PlaceholderState.Retrieving;
- loading.Show();
-
if (Scope != LeaderboardScope.Global && !api.LocalUser.Value.IsSupporter)
{
- loading.Hide();
PlaceholderState = PlaceholderState.NotSupporter;
return;
}
+ PlaceholderState = PlaceholderState.Retrieving;
+ loading.Show();
+
getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value ?? Beatmap.Ruleset, Scope);
getScoresRequest.Success += r =>
{
diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
index 377f32184d..857a716ab7 100644
--- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
@@ -146,7 +146,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/Options/BeatmapOptionsOverlay.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs
index c66cc7beff..789064a5f1 100644
--- a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs
+++ b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs
@@ -25,6 +25,8 @@ namespace osu.Game.Screens.Select.Options
private readonly Box holder;
private readonly FillFlowContainer buttonsContainer;
+ public override bool BlockScreenWideMouse => false;
+
protected override void PopIn()
{
base.PopIn();
diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs
index 727cdb9959..a3997640ba 100644
--- a/osu.Game/Screens/Select/PlaySongSelect.cs
+++ b/osu.Game/Screens/Select/PlaySongSelect.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Screens.Select
{
private OsuScreen player;
private readonly ModSelectOverlay modSelect;
- private readonly BeatmapDetailArea beatmapDetails;
+ protected readonly BeatmapDetailArea BeatmapDetails;
private bool removeAutoModOnResume;
public PlaySongSelect()
@@ -35,13 +35,13 @@ namespace osu.Game.Screens.Select
Anchor = Anchor.BottomCentre,
});
- LeftContent.Add(beatmapDetails = new BeatmapDetailArea
+ LeftContent.Add(BeatmapDetails = new BeatmapDetailArea
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 10, Right = 5 },
});
- beatmapDetails.Leaderboard.ScoreSelected += s => Push(new Results(s));
+ BeatmapDetails.Leaderboard.ScoreSelected += s => Push(new Results(s));
}
private SampleChannel sampleConfirm;
@@ -78,7 +78,7 @@ namespace osu.Game.Screens.Select
beatmap.Mods.BindTo(modSelect.SelectedMods);
- beatmapDetails.Beatmap = beatmap;
+ BeatmapDetails.Beatmap = beatmap;
if (beatmap.Track != null)
beatmap.Track.Looping = true;
@@ -124,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)
@@ -147,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 9e5a2fa633..919cfcfbe4 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -227,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.SelectBeatmap(beatmap);
+ if (beatmap != null)
+ Carousel.SelectBeatmap(beatmap);
if (selectionChangedDebounce?.Completed == false)
{
@@ -242,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;
@@ -339,7 +345,7 @@ namespace osu.Game.Screens.Select
logo.Action = () =>
{
- Start();
+ FinaliseSelection();
return false;
};
}
@@ -443,9 +449,16 @@ namespace osu.Game.Screens.Select
private void carouselBeatmapsLoaded()
{
if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false)
+ {
Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo);
+ }
else if (Carousel.SelectedBeatmapSet == null)
- Carousel.SelectNextRandom();
+ {
+ if (!Carousel.SelectNextRandom())
+ // in the case random selection failed, we want to trigger selectionChanged
+ // to show the dummy beatmap (we have nothing else to display).
+ carouselSelectionChanged(null);
+ }
}
private void delete(BeatmapSetInfo beatmap)
@@ -462,7 +475,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 6a8a361ea0..08f27dfde9 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 8984fc843f..8b93be9bb5 100644
--- a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs
+++ b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs
@@ -7,6 +7,7 @@ using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Caching;
+using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -203,6 +204,8 @@ namespace osu.Game.Tests.Visual
private readonly FillFlowContainer scores;
private APIAccess api;
+ private readonly Bindable currentBeatmap = new Bindable();
+
public PerformanceList()
{
RelativeSizeAxes = Axes.X;
@@ -231,12 +234,15 @@ namespace osu.Game.Tests.Visual
};
}
- osuGame.Beatmap.ValueChanged += beatmapChanged;
+ currentBeatmap.ValueChanged += beatmapChanged;
+ currentBeatmap.BindTo(osuGame.Beatmap);
}
private GetScoresRequest lastRequest;
private void beatmapChanged(WorkingBeatmap newBeatmap)
{
+ if (!IsAlive) return;
+
lastRequest?.Cancel();
scores.Clear();
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 a2cc8e8d49..e0a4e3184d 100644
--- a/osu.Game/Users/UserPanel.cs
+++ b/osu.Game/Users/UserPanel.cs
@@ -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,