diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index e2fc4d14f6..803927bc6f 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -27,9 +27,9 @@
-
-
-
+
+
+
diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
index d49be69856..102ec7fb3b 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
@@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Catch.UI
private readonly CatcherArea catcherArea;
+ protected override bool UserScrollSpeedAdjustment => false;
+
public CatchPlayfield(BeatmapDifficulty difficulty, Func> getVisualRepresentation)
: base(BASE_WIDTH)
{
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
index 30837d7db9..440b314e5f 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
@@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Mods
public override ModType Type => ModType.Fun;
public override string Description => "Everything rotates. EVERYTHING.";
public override double ScoreMultiplier => 1;
+ public override Type[] IncompatibleMods => new[] { typeof(OsuModWiggle) };
+
private float theta;
public void ApplyToDrawableHitObjects(IEnumerable drawables)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
index 5b69247451..2e601c9078 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
@@ -21,6 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override ModType Type => ModType.Fun;
public override string Description => "They just won't stay still...";
public override double ScoreMultiplier => 1;
+ public override Type[] IncompatibleMods => new[] { typeof(OsuModTransform) };
private const int wiggle_duration = 90; // (ms) Higher = fewer wiggles
private const int wiggle_strength = 10; // Higher = stronger wiggles
diff --git a/osu.Game.Tests/Visual/TestCaseKeyCounter.cs b/osu.Game.Tests/Visual/TestCaseKeyCounter.cs
index b98875cd6a..f31a687d2d 100644
--- a/osu.Game.Tests/Visual/TestCaseKeyCounter.cs
+++ b/osu.Game.Tests/Visual/TestCaseKeyCounter.cs
@@ -1,9 +1,14 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
+using System.Collections.Generic;
+using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
+using osu.Framework.Input.EventArgs;
using osu.Framework.MathUtils;
+using osu.Framework.Timing;
using osu.Game.Screens.Play;
using OpenTK.Input;
@@ -12,21 +17,30 @@ namespace osu.Game.Tests.Visual
[TestFixture]
public class TestCaseKeyCounter : OsuTestCase
{
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(KeyCounterKeyboard),
+ typeof(KeyCounterMouse),
+ typeof(KeyCounterCollection)
+ };
+
public TestCaseKeyCounter()
{
+ KeyCounterKeyboard rewindTestKeyCounterKeyboard;
KeyCounterCollection kc = new KeyCounterCollection
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new KeyCounter[]
{
- new KeyCounterKeyboard(Key.Z),
+ rewindTestKeyCounterKeyboard = new KeyCounterKeyboard(Key.X),
new KeyCounterKeyboard(Key.X),
new KeyCounterMouse(MouseButton.Left),
new KeyCounterMouse(MouseButton.Right),
},
};
+
AddStep("Add random", () =>
{
Key key = (Key)((int)Key.A + RNG.Next(26));
@@ -34,7 +48,57 @@ namespace osu.Game.Tests.Visual
});
AddSliderStep("Fade time", 0, 200, 50, v => kc.FadeTime = v);
+ Key testKey = ((KeyCounterKeyboard)kc.Children.First()).Key;
+ double time1 = 0;
+
+ AddStep($"Press {testKey} key", () =>
+ {
+ rewindTestKeyCounterKeyboard.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = testKey, Repeat = false });
+ rewindTestKeyCounterKeyboard.TriggerOnKeyUp(null, new KeyUpEventArgs { Key = testKey });
+ });
+
+ AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 1);
+
+ AddStep($"Press {testKey} key", () =>
+ {
+ rewindTestKeyCounterKeyboard.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = testKey, Repeat = false });
+ rewindTestKeyCounterKeyboard.TriggerOnKeyUp(null, new KeyUpEventArgs { Key = testKey });
+ time1 = Clock.CurrentTime;
+ });
+
+ AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 2);
+
+ IFrameBasedClock oldClock = null;
+
+ AddStep($"Rewind {testKey} counter once", () =>
+ {
+ oldClock = rewindTestKeyCounterKeyboard.Clock;
+ rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(time1 - 10));
+ });
+
+ AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 1);
+
+ AddStep($"Rewind {testKey} counter to zero", () => rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(0)));
+
+ AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 0);
+
+ AddStep("Restore clock", () => rewindTestKeyCounterKeyboard.Clock = oldClock);
+
Add(kc);
}
+
+ private class FixedClock : IClock
+ {
+ private readonly double time;
+
+ public FixedClock(double time)
+ {
+ this.time = time;
+ }
+
+ public double CurrentTime => time;
+ public double Rate => 1;
+ public bool IsRunning => false;
+ }
}
}
diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs
index f0f765a4c9..5ad6427fd8 100644
--- a/osu.Game/Screens/Menu/OsuLogo.cs
+++ b/osu.Game/Screens/Menu/OsuLogo.cs
@@ -84,11 +84,10 @@ namespace osu.Game.Screens.Menu
private const double early_activation = 60;
+ public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
+
public OsuLogo()
{
- // Required to make Schedule calls run in OsuScreen even when we are not visible.
- AlwaysPresent = true;
-
EarlyActivationMilliseconds = early_activation;
Size = new Vector2(default_size);
diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs
index f51ea6fe3e..eb137f5447 100644
--- a/osu.Game/Screens/Play/HUDOverlay.cs
+++ b/osu.Game/Screens/Play/HUDOverlay.cs
@@ -69,7 +69,7 @@ namespace osu.Game.Screens.Play
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- KeyCounter = CreateKeyCounter(),
+ KeyCounter = CreateKeyCounter(adjustableClock as IFrameBasedClock),
HoldToQuit = CreateQuitButton(),
}
}
@@ -194,12 +194,13 @@ namespace osu.Game.Screens.Play
Margin = new MarginPadding { Top = 20 }
};
- protected virtual KeyCounterCollection CreateKeyCounter() => new KeyCounterCollection
+ protected virtual KeyCounterCollection CreateKeyCounter(IFrameBasedClock offsetClock) => new KeyCounterCollection
{
FadeTime = 50,
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Margin = new MarginPadding(10),
+ AudioClock = offsetClock
};
protected virtual ScoreCounter CreateScoreCounter() => new ScoreCounter(6)
diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs
index 2c31e61114..d1efe4cab7 100644
--- a/osu.Game/Screens/Play/KeyCounter.cs
+++ b/osu.Game/Screens/Play/KeyCounter.cs
@@ -1,6 +1,8 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -19,6 +21,9 @@ namespace osu.Game.Screens.Play
private Container textLayer;
private SpriteText countSpriteText;
+ private readonly List states = new List();
+ private KeyCounterState currentState;
+
public bool IsCounting { get; set; } = true;
private int countPresses;
public int CountPresses
@@ -45,7 +50,10 @@ namespace osu.Game.Screens.Play
isLit = value;
updateGlowSprite(value);
if (value && IsCounting)
+ {
CountPresses++;
+ saveState();
+ }
}
}
}
@@ -128,6 +136,32 @@ namespace osu.Game.Screens.Play
}
}
- public void ResetCount() => CountPresses = 0;
+ public void ResetCount()
+ {
+ CountPresses = 0;
+ states.Clear();
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (currentState?.Time > Clock.CurrentTime)
+ restoreStateTo(Clock.CurrentTime);
+ }
+
+ private void saveState()
+ {
+ if (currentState == null || currentState.Time < Clock.CurrentTime)
+ states.Add(currentState = new KeyCounterState(Clock.CurrentTime, CountPresses));
+ }
+
+ private void restoreStateTo(double time)
+ {
+ states.RemoveAll(state => state.Time > time);
+
+ currentState = states.LastOrDefault();
+ CountPresses = currentState?.Count ?? 0;
+ }
}
}
diff --git a/osu.Game/Screens/Play/KeyCounterCollection.cs b/osu.Game/Screens/Play/KeyCounterCollection.cs
index 5cb5bb152a..2a737d974b 100644
--- a/osu.Game/Screens/Play/KeyCounterCollection.cs
+++ b/osu.Game/Screens/Play/KeyCounterCollection.cs
@@ -3,15 +3,16 @@
using System;
using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using OpenTK.Graphics;
-using osu.Framework.Configuration;
-using osu.Framework.Allocation;
using osu.Framework.Input.EventArgs;
using osu.Framework.Input.States;
+using osu.Framework.Timing;
using osu.Game.Configuration;
using OpenTK;
+using OpenTK.Graphics;
namespace osu.Game.Screens.Play
{
@@ -37,6 +38,9 @@ namespace osu.Game.Screens.Play
key.FadeTime = FadeTime;
key.KeyDownTextColor = KeyDownTextColor;
key.KeyUpTextColor = KeyUpTextColor;
+ // Use the same clock object as SongProgress for saving KeyCounter state
+ if (AudioClock != null)
+ key.Clock = AudioClock;
}
public void ResetCount()
@@ -118,6 +122,8 @@ namespace osu.Game.Screens.Play
public override bool HandleKeyboardInput => receptor == null;
public override bool HandleMouseInput => receptor == null;
+ public IFrameBasedClock AudioClock { get; set; }
+
private Receptor receptor;
public Receptor GetReceptor()
diff --git a/osu.Game/Screens/Play/KeyCounterState.cs b/osu.Game/Screens/Play/KeyCounterState.cs
new file mode 100644
index 0000000000..e5c0703319
--- /dev/null
+++ b/osu.Game/Screens/Play/KeyCounterState.cs
@@ -0,0 +1,17 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Screens.Play
+{
+ public class KeyCounterState
+ {
+ public KeyCounterState(double time, int count)
+ {
+ Time = time;
+ Count = count;
+ }
+
+ public readonly double Time;
+ public readonly int Count;
+ }
+}
diff --git a/osu.Game/Storyboards/CommandTimeline.cs b/osu.Game/Storyboards/CommandTimeline.cs
index 7de0756dbd..8a032064d3 100644
--- a/osu.Game/Storyboards/CommandTimeline.cs
+++ b/osu.Game/Storyboards/CommandTimeline.cs
@@ -16,10 +16,10 @@ namespace osu.Game.Storyboards
public bool HasCommands => commands.Count > 0;
private Cached startTimeBacking;
- public double StartTime => startTimeBacking.IsValid ? startTimeBacking : (startTimeBacking.Value = HasCommands ? commands.Min(c => c.StartTime) : double.MinValue);
+ public double StartTime => startTimeBacking.IsValid ? startTimeBacking : startTimeBacking.Value = HasCommands ? commands.Min(c => c.StartTime) : double.MinValue;
private Cached endTimeBacking;
- public double EndTime => endTimeBacking.IsValid ? endTimeBacking : (endTimeBacking.Value = HasCommands ? commands.Max(c => c.EndTime) : double.MaxValue);
+ public double EndTime => endTimeBacking.IsValid ? endTimeBacking : endTimeBacking.Value = HasCommands ? commands.Max(c => c.EndTime) : double.MaxValue;
public T StartValue => HasCommands ? commands.OrderBy(c => c.StartTime).First().StartValue : default(T);
public T EndValue => HasCommands ? commands.OrderByDescending(c => c.EndTime).First().EndValue : default(T);
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 83ab5534c6..05291cf3d0 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/osu.TestProject.props b/osu.TestProject.props
index 58de6ec030..506d634555 100644
--- a/osu.TestProject.props
+++ b/osu.TestProject.props
@@ -11,7 +11,7 @@
-
+