diff --git a/osu.Android.props b/osu.Android.props
index 78ceaa8616..d7817cf4cf 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,6 +52,6 @@
-
+
diff --git a/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs
index cc01009dd9..75feb21298 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs
@@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Catch.UI
if (!result.Type.AffectsCombo() || !result.HasResult)
return;
- if (result.Type == HitResult.Miss)
+ if (!result.IsHit)
{
updateCombo(0, null);
return;
diff --git a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs
index 220dedc4a4..d28b7bdf58 100644
--- a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs
+++ b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs
@@ -2,10 +2,40 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Judgements
{
public class ManiaJudgement : Judgement
{
+ protected override double HealthIncreaseFor(HitResult result)
+ {
+ switch (result)
+ {
+ case HitResult.LargeTickHit:
+ return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
+
+ case HitResult.LargeTickMiss:
+ return -DEFAULT_MAX_HEALTH_INCREASE * 0.1;
+
+ case HitResult.Meh:
+ return -DEFAULT_MAX_HEALTH_INCREASE * 0.5;
+
+ case HitResult.Ok:
+ return -DEFAULT_MAX_HEALTH_INCREASE * 0.3;
+
+ case HitResult.Good:
+ return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
+
+ case HitResult.Great:
+ return DEFAULT_MAX_HEALTH_INCREASE * 0.8;
+
+ case HitResult.Perfect:
+ return DEFAULT_MAX_HEALTH_INCREASE;
+
+ default:
+ return base.HealthIncreaseFor(result);
+ }
+ }
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
index ba6cad978d..f6d539c91b 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
@@ -243,7 +243,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
endHold();
}
- if (Tail.Result.Type == HitResult.Miss)
+ if (Tail.Judged && !Tail.IsHit)
HasBroken = true;
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs
index 31e43d3ee2..c780c0836e 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs
@@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
- ApplyResult(r => r.Type = HitResult.Miss);
+ ApplyResult(r => r.Type = r.Judgement.MinResult);
return;
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
index 08c41b0d75..27960b3f3a 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
@@ -9,7 +9,6 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Rulesets.Mania.UI;
-using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
@@ -136,7 +135,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
///
/// Causes this to get missed, disregarding all conditions in implementations of .
///
- public void MissForcefully() => ApplyResult(r => r.Type = HitResult.Miss);
+ public void MissForcefully() => ApplyResult(r => r.Type = r.Judgement.MinResult);
}
public abstract class DrawableManiaHitObject : DrawableManiaHitObject
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
index 973dc06e05..b3402d13e4 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
@@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
- ApplyResult(r => r.Type = HitResult.Miss);
+ ApplyResult(r => r.Type = r.Judgement.MinResult);
return;
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs
index f3221ffe32..39deba2f57 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs
@@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
HitObjects = { new HitCircle { Position = new Vector2(256, 192) } }
},
- PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset < -hitWindows.WindowFor(HitResult.Meh) && Player.Results[0].Type == HitResult.Miss
+ PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset < -hitWindows.WindowFor(HitResult.Meh) && !Player.Results[0].IsHit
});
}
@@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
Autoplay = false,
Beatmap = beatmap,
- PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset >= hitWindows.WindowFor(HitResult.Meh) && Player.Results[0].Type == HitResult.Miss
+ PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset >= hitWindows.WindowFor(HitResult.Meh) && !Player.Results[0].IsHit
});
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
index d5c3538c81..0164fb8bf4 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
@@ -314,11 +314,11 @@ namespace osu.Game.Rulesets.Osu.Tests
private bool assertMaxJudge() => judgementResults.Any() && judgementResults.All(t => t.Type == t.Judgement.MaxResult);
- private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.IgnoreHit && judgementResults.First().Type == HitResult.Miss;
+ private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.SmallTickHit && !judgementResults.First().IsHit;
- private bool assertMidSliderJudgements() => judgementResults[^2].Type == HitResult.IgnoreHit;
+ private bool assertMidSliderJudgements() => judgementResults[^2].Type == HitResult.SmallTickHit;
- private bool assertMidSliderJudgementFail() => judgementResults[^2].Type == HitResult.IgnoreMiss;
+ private bool assertMidSliderJudgementFail() => judgementResults[^2].Type == HitResult.SmallTickMiss;
private ScoreAccessibleReplayPlayer currentPlayer;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index a438dc8be4..b5ac26c824 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
- ApplyResult(r => r.Type = HitResult.Miss);
+ ApplyResult(r => r.Type = r.Judgement.MinResult);
return;
}
@@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
var circleResult = (OsuHitCircleJudgementResult)r;
// Todo: This should also consider misses, but they're a little more interesting to handle, since we don't necessarily know the position at the time of a miss.
- if (result != HitResult.Miss)
+ if (result.IsHit())
{
var localMousePosition = ToLocalSpace(inputManager.CurrentState.Mouse.Position);
circleResult.CursorPositionAtHit = HitObject.StackedPosition + (localMousePosition - DrawSize / 2);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
index 2946331bc6..45c664ba3b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
@@ -8,7 +8,6 @@ using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Osu.UI;
-using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -68,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
///
/// Causes this to get missed, disregarding all conditions in implementations of .
///
- public void MissForcefully() => ApplyResult(r => r.Type = HitResult.Miss);
+ public void MissForcefully() => ApplyResult(r => r.Type = r.Judgement.MinResult);
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(HitObject, judgement);
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
index 012d9f8878..49535e7fff 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
@@ -8,7 +8,6 @@ using osu.Game.Configuration;
using osuTK;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osuTK.Graphics;
@@ -67,7 +66,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (JudgedObject != null)
{
lightingColour = JudgedObject.AccentColour.GetBoundCopy();
- lightingColour.BindValueChanged(colour => Lighting.Colour = Result.Type == HitResult.Miss ? Color4.Transparent : colour.NewValue, true);
+ lightingColour.BindValueChanged(colour => Lighting.Colour = Result.IsHit ? colour.NewValue : Color4.Transparent, true);
}
else
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 9abcef83c4..280ca33234 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -13,7 +13,6 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Osu.UI;
-using osu.Game.Rulesets.Scoring;
using osuTK.Graphics;
using osu.Game.Skinning;
@@ -250,7 +249,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
// rather than doing it this way, we should probably attach the sample to the tail circle.
// this can only be done after we stop using LegacyLastTick.
- if (TailCircle.Result.Type != HitResult.Miss)
+ if (TailCircle.IsHit)
base.PlaySamples();
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index fe7cb278b0..936bfaeb86 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
return;
// Trigger a miss result for remaining ticks to avoid infinite gameplay.
- foreach (var tick in ticks.Where(t => !t.IsHit))
+ foreach (var tick in ticks.Where(t => !t.Result.HasResult))
tick.TriggerResult(false);
ApplyResult(r =>
@@ -224,7 +224,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
else if (Progress > .75)
r.Type = HitResult.Meh;
else if (Time.Current >= Spinner.EndTime)
- r.Type = HitResult.Miss;
+ r.Type = r.Judgement.MinResult;
});
}
@@ -268,7 +268,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
while (wholeSpins != spins)
{
- var tick = ticks.FirstOrDefault(t => !t.IsHit);
+ var tick = ticks.FirstOrDefault(t => !t.Result.HasResult);
// tick may be null if we've hit the spin limit.
if (tick != null)
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
index aff3f38e17..3afd36669f 100644
--- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
@@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public class SliderTailJudgement : OsuJudgement
{
- public override HitResult MaxResult => HitResult.IgnoreHit;
+ public override HitResult MaxResult => HitResult.SmallTickHit;
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs
index 0551df3211..647ad7853d 100644
--- a/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{
switch (result)
{
- case HitResult.Great:
+ case HitResult.SmallTickHit:
return 0.15;
default:
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
index 286feac5ba..8f268dc1c7 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
@@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (!(obj is DrawableDrumRollTick))
return;
- if (result.Type > HitResult.Miss)
+ if (result.IsHit)
rollingHits++;
else
rollingHits--;
@@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
ApplyResult(r => r.Type = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Ok);
}
else
- ApplyResult(r => r.Type = HitResult.Miss);
+ ApplyResult(r => r.Type = r.Judgement.MinResult);
}
protected override void UpdateStateTransforms(ArmedState state)
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
index 03df28f850..bb42240f25 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
@@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
- ApplyResult(r => r.Type = HitResult.Miss);
+ ApplyResult(r => r.Type = r.Judgement.MinResult);
return;
}
@@ -152,7 +152,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
return;
if (!validActionPressed)
- ApplyResult(r => r.Type = HitResult.Miss);
+ ApplyResult(r => r.Type = r.Judgement.MinResult);
else
ApplyResult(r => r.Type = result);
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
index 11ff0729e2..8ee4a5db71 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
@@ -211,9 +211,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
tick.TriggerResult(false);
}
- var hitResult = numHits > HitObject.RequiredHits / 2 ? HitResult.Ok : HitResult.Miss;
-
- ApplyResult(r => r.Type = hitResult);
+ ApplyResult(r => r.Type = numHits > HitObject.RequiredHits / 2 ? HitResult.Ok : r.Judgement.MinResult);
}
}
diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs
index dd3c2289ea..f7a1d130eb 100644
--- a/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs
+++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
private double hpMultiplier;
///
- /// HP multiplier for a .
+ /// HP multiplier for a that does not satisfy .
///
private double hpMissMultiplier;
@@ -45,6 +45,6 @@ namespace osu.Game.Rulesets.Taiko.Scoring
}
protected override double GetHealthIncreaseFor(JudgementResult result)
- => base.GetHealthIncreaseFor(result) * (result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier);
+ => base.GetHealthIncreaseFor(result) * (result.IsHit ? hpMultiplier : hpMissMultiplier);
}
}
diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs
index 928072c491..e029040ef3 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs
@@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning
if (r?.Type.AffectsCombo() == false)
return;
- passing = r == null || r.Type > HitResult.Miss;
+ passing = r == null || r.IsHit;
foreach (var sprite in InternalChildren.OfType())
sprite.Passing = passing;
diff --git a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs
index 7b8ab89233..3bd20e4bb4 100644
--- a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs
+++ b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs
@@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.UI
Alpha = 0.15f;
Masking = true;
- if (result == HitResult.Miss)
+ if (!result.IsHit())
return;
bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim;
diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
index 5966b24b34..1ca1be1bdf 100644
--- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
+++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
@@ -163,16 +163,14 @@ namespace osu.Game.Rulesets.Taiko.UI
target = centreHit;
back = centre;
- if (gameplayClock?.IsSeeking != true)
- drumSample.Centre?.Play();
+ drumSample.Centre?.Play();
}
else if (action == RimAction)
{
target = rimHit;
back = rim;
- if (gameplayClock?.IsSeeking != true)
- drumSample.Rim?.Play();
+ drumSample.Rim?.Play();
}
if (target != null)
diff --git a/osu.Game.Tests/NonVisual/GameplayClockTest.cs b/osu.Game.Tests/NonVisual/GameplayClockTest.cs
new file mode 100644
index 0000000000..3fd7c364b7
--- /dev/null
+++ b/osu.Game.Tests/NonVisual/GameplayClockTest.cs
@@ -0,0 +1,39 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using NUnit.Framework;
+using osu.Framework.Bindables;
+using osu.Framework.Timing;
+using osu.Game.Screens.Play;
+
+namespace osu.Game.Tests.NonVisual
+{
+ [TestFixture]
+ public class GameplayClockTest
+ {
+ [TestCase(0)]
+ [TestCase(1)]
+ public void TestTrueGameplayRateWithZeroAdjustment(double underlyingClockRate)
+ {
+ var framedClock = new FramedClock(new ManualClock { Rate = underlyingClockRate });
+ var gameplayClock = new TestGameplayClock(framedClock);
+
+ gameplayClock.MutableNonGameplayAdjustments.Add(new BindableDouble());
+
+ Assert.That(gameplayClock.TrueGameplayRate, Is.EqualTo(0));
+ }
+
+ private class TestGameplayClock : GameplayClock
+ {
+ public List> MutableNonGameplayAdjustments { get; } = new List>();
+
+ public override IEnumerable> NonGameplayAdjustments => MutableNonGameplayAdjustments;
+
+ public TestGameplayClock(IFrameBasedClock underlyingClock)
+ : base(underlyingClock)
+ {
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs
index 720cf51f2c..13a3195824 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs
@@ -5,6 +5,8 @@ using System;
using System.IO;
using System.Linq;
using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
@@ -22,6 +24,9 @@ namespace osu.Game.Tests.Visual.Editing
protected override bool EditorComponentsReady => Editor.ChildrenOfType().SingleOrDefault()?.IsLoaded == true;
+ [Resolved]
+ private BeatmapManager beatmapManager { get; set; }
+
public override void SetUpSteps()
{
AddStep("set dummy", () => Beatmap.Value = new DummyWorkingBeatmap(Audio, null));
@@ -38,6 +43,15 @@ namespace osu.Game.Tests.Visual.Editing
{
AddStep("save beatmap", () => Editor.Save());
AddAssert("new beatmap persisted", () => EditorBeatmap.BeatmapInfo.ID > 0);
+ AddAssert("new beatmap in database", () => beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID)?.DeletePending == false);
+ }
+
+ [Test]
+ public void TestExitWithoutSave()
+ {
+ AddStep("exit without save", () => Editor.Exit());
+ AddUntilStep("wait for exit", () => !Editor.IsCurrentScreen());
+ AddAssert("new beatmap not persisted", () => beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID)?.DeletePending == true);
}
[Test]
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSamplePlayback.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSamplePlayback.cs
index 039a21fd94..f182023c0e 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSamplePlayback.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSamplePlayback.cs
@@ -19,12 +19,14 @@ namespace osu.Game.Tests.Visual.Editing
public void TestSlidingSampleStopsOnSeek()
{
DrawableSlider slider = null;
- DrawableSample[] samples = null;
+ DrawableSample[] loopingSamples = null;
+ DrawableSample[] onceOffSamples = null;
AddStep("get first slider", () =>
{
slider = Editor.ChildrenOfType().OrderBy(s => s.HitObject.StartTime).First();
- samples = slider.ChildrenOfType().ToArray();
+ onceOffSamples = slider.ChildrenOfType().Where(s => !s.Looping).ToArray();
+ loopingSamples = slider.ChildrenOfType().Where(s => s.Looping).ToArray();
});
AddStep("start playback", () => EditorClock.Start());
@@ -34,14 +36,15 @@ namespace osu.Game.Tests.Visual.Editing
if (!slider.Tracking.Value)
return false;
- if (!samples.Any(s => s.Playing))
+ if (!loopingSamples.Any(s => s.Playing))
return false;
EditorClock.Seek(20000);
return true;
});
- AddAssert("slider samples are not playing", () => samples.Length == 5 && samples.All(s => s.Played && !s.Playing));
+ AddAssert("non-looping samples are playing", () => onceOffSamples.Length == 4 && loopingSamples.All(s => s.Played || s.Playing));
+ AddAssert("looping samples are not playing", () => loopingSamples.Length == 1 && loopingSamples.All(s => s.Played && !s.Playing));
}
}
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySamplePlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySamplePlayback.cs
new file mode 100644
index 0000000000..3ab4df20df
--- /dev/null
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySamplePlayback.cs
@@ -0,0 +1,61 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Graphics.Audio;
+using osu.Framework.Testing;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Rulesets.UI;
+using osu.Game.Screens.Play;
+using osu.Game.Skinning;
+
+namespace osu.Game.Tests.Visual.Gameplay
+{
+ public class TestSceneGameplaySamplePlayback : PlayerTestScene
+ {
+ [Test]
+ public void TestAllSamplesStopDuringSeek()
+ {
+ DrawableSlider slider = null;
+ DrawableSample[] samples = null;
+ ISamplePlaybackDisabler gameplayClock = null;
+
+ AddStep("get variables", () =>
+ {
+ gameplayClock = Player.ChildrenOfType().First().GameplayClock;
+ slider = Player.ChildrenOfType().OrderBy(s => s.HitObject.StartTime).First();
+ samples = slider.ChildrenOfType().ToArray();
+ });
+
+ AddUntilStep("wait for slider sliding then seek", () =>
+ {
+ if (!slider.Tracking.Value)
+ return false;
+
+ if (!samples.Any(s => s.Playing))
+ return false;
+
+ Player.ChildrenOfType().First().Seek(40000);
+ return true;
+ });
+
+ AddAssert("sample playback disabled", () => gameplayClock.SamplePlaybackDisabled.Value);
+
+ // because we are in frame stable context, it's quite likely that not all samples are "played" at this point.
+ // the important thing is that at least one started, and that sample has since stopped.
+ AddAssert("no samples are playing", () => Player.ChildrenOfType().All(s => !s.IsPlaying));
+
+ AddAssert("sample playback still disabled", () => gameplayClock.SamplePlaybackDisabled.Value);
+
+ AddUntilStep("seek finished, sample playback enabled", () => !gameplayClock.SamplePlaybackDisabled.Value);
+ AddUntilStep("any sample is playing", () => Player.ChildrenOfType().Any(s => s.IsPlaying));
+ }
+
+ protected override bool Autoplay => true;
+
+ protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
+ }
+}
diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs
index 4ee0ce437c..89a3a2b855 100644
--- a/osu.Game/Rulesets/Judgements/Judgement.cs
+++ b/osu.Game/Rulesets/Judgements/Judgement.cs
@@ -109,40 +109,40 @@ namespace osu.Game.Rulesets.Judgements
return 0;
case HitResult.SmallTickHit:
- return DEFAULT_MAX_HEALTH_INCREASE * 0.05;
+ return DEFAULT_MAX_HEALTH_INCREASE * 0.5;
case HitResult.SmallTickMiss:
- return -DEFAULT_MAX_HEALTH_INCREASE * 0.05;
+ return -DEFAULT_MAX_HEALTH_INCREASE * 0.5;
case HitResult.LargeTickHit:
- return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
+ return DEFAULT_MAX_HEALTH_INCREASE;
case HitResult.LargeTickMiss:
- return -DEFAULT_MAX_HEALTH_INCREASE * 0.1;
+ return -DEFAULT_MAX_HEALTH_INCREASE;
case HitResult.Miss:
return -DEFAULT_MAX_HEALTH_INCREASE;
case HitResult.Meh:
- return -DEFAULT_MAX_HEALTH_INCREASE * 0.5;
+ return -DEFAULT_MAX_HEALTH_INCREASE * 0.05;
case HitResult.Ok:
- return -DEFAULT_MAX_HEALTH_INCREASE * 0.3;
+ return DEFAULT_MAX_HEALTH_INCREASE * 0.5;
case HitResult.Good:
- return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
+ return DEFAULT_MAX_HEALTH_INCREASE * 0.75;
case HitResult.Great:
- return DEFAULT_MAX_HEALTH_INCREASE * 0.8;
-
- case HitResult.Perfect:
return DEFAULT_MAX_HEALTH_INCREASE;
+ case HitResult.Perfect:
+ return DEFAULT_MAX_HEALTH_INCREASE * 1.05;
+
case HitResult.SmallBonus:
- return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
+ return DEFAULT_MAX_HEALTH_INCREASE * 0.5;
case HitResult.LargeBonus:
- return DEFAULT_MAX_HEALTH_INCREASE * 0.2;
+ return DEFAULT_MAX_HEALTH_INCREASE;
}
}
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index 29c25f20a4..8012b4d95c 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -385,9 +385,14 @@ namespace osu.Game.Rulesets.Objects.Drawables
}
///
- /// Stops playback of all samples. Automatically called when 's lifetime has been exceeded.
+ /// Stops playback of all relevant samples. Generally only looping samples should be stopped by this, and the rest let to play out.
+ /// Automatically called when 's lifetime has been exceeded.
///
- public virtual void StopAllSamples() => Samples?.Stop();
+ public virtual void StopAllSamples()
+ {
+ if (Samples?.Looping == true)
+ Samples.Stop();
+ }
protected override void Update()
{
@@ -457,6 +462,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
foreach (var nested in NestedHitObjects)
nested.OnKilled();
+ // failsafe to ensure looping samples don't get stuck in a playing state.
+ // this could occur in a non-frame-stable context where DrawableHitObjects get killed before a SkinnableSound has the chance to be stopped.
StopAllSamples();
UpdateResult(false);
@@ -506,19 +513,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
Result.TimeOffset = Math.Min(HitObject.HitWindows.WindowFor(HitResult.Miss), Time.Current - endTime);
- switch (Result.Type)
- {
- case HitResult.None:
- break;
-
- case HitResult.Miss:
- updateState(ArmedState.Miss);
- break;
-
- default:
- updateState(ArmedState.Hit);
- break;
- }
+ if (Result.HasResult)
+ updateState(Result.IsHit ? ArmedState.Hit : ArmedState.Miss);
OnNewResult?.Invoke(this, Result);
}
diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
index 55c4edfbd1..70b3d0c7d4 100644
--- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
+++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
@@ -35,6 +35,7 @@ namespace osu.Game.Rulesets.UI
public GameplayClock GameplayClock => stabilityGameplayClock;
[Cached(typeof(GameplayClock))]
+ [Cached(typeof(ISamplePlaybackDisabler))]
private readonly StabilityGameplayClock stabilityGameplayClock;
public FrameStabilityContainer(double gameplayStartTime = double.MinValue)
@@ -58,13 +59,16 @@ namespace osu.Game.Rulesets.UI
private int direction;
[BackgroundDependencyLoader(true)]
- private void load(GameplayClock clock)
+ private void load(GameplayClock clock, ISamplePlaybackDisabler sampleDisabler)
{
if (clock != null)
{
parentGameplayClock = stabilityGameplayClock.ParentGameplayClock = clock;
GameplayClock.IsPaused.BindTo(clock.IsPaused);
}
+
+ // this is a bit temporary. should really be done inside of GameplayClock (but requires large structural changes).
+ stabilityGameplayClock.ParentSampleDisabler = sampleDisabler;
}
protected override void LoadComplete()
@@ -207,11 +211,15 @@ namespace osu.Game.Rulesets.UI
private void setClock()
{
- // in case a parent gameplay clock isn't available, just use the parent clock.
- parentGameplayClock ??= Clock;
-
- Clock = GameplayClock;
- ProcessCustomClock = false;
+ if (parentGameplayClock == null)
+ {
+ // in case a parent gameplay clock isn't available, just use the parent clock.
+ parentGameplayClock ??= Clock;
+ }
+ else
+ {
+ Clock = GameplayClock;
+ }
}
public ReplayInputHandler ReplayInputHandler { get; set; }
@@ -220,6 +228,8 @@ namespace osu.Game.Rulesets.UI
{
public GameplayClock ParentGameplayClock;
+ public ISamplePlaybackDisabler ParentSampleDisabler;
+
public override IEnumerable> NonGameplayAdjustments => ParentGameplayClock?.NonGameplayAdjustments ?? Enumerable.Empty>();
public StabilityGameplayClock(FramedClock underlyingClock)
@@ -227,7 +237,11 @@ namespace osu.Game.Rulesets.UI
{
}
- public override bool IsSeeking => ParentGameplayClock != null && Math.Abs(CurrentTime - ParentGameplayClock.CurrentTime) > 200;
+ protected override bool ShouldDisableSamplePlayback =>
+ // handle the case where playback is catching up to real-time.
+ base.ShouldDisableSamplePlayback
+ || ParentSampleDisabler?.SamplePlaybackDisabled.Value == true
+ || (ParentGameplayClock != null && Math.Abs(CurrentTime - ParentGameplayClock.CurrentTime) > 200);
}
}
}
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index a0692d94e6..875ab25003 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -84,6 +84,8 @@ namespace osu.Game.Screens.Edit
private DependencyContainer dependencies;
+ private bool isNewBeatmap;
+
protected override UserActivity InitialActivity => new UserActivity.Editing(Beatmap.Value.BeatmapInfo);
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
@@ -113,8 +115,6 @@ namespace osu.Game.Screens.Edit
// todo: remove caching of this and consume via editorBeatmap?
dependencies.Cache(beatDivisor);
- bool isNewBeatmap = false;
-
if (Beatmap.Value is DummyWorkingBeatmap)
{
isNewBeatmap = true;
@@ -287,6 +287,9 @@ namespace osu.Game.Screens.Edit
protected void Save()
{
+ // no longer new after first user-triggered save.
+ isNewBeatmap = false;
+
// apply any set-level metadata changes.
beatmapManager.Update(playableBeatmap.BeatmapInfo.BeatmapSet);
@@ -435,10 +438,20 @@ namespace osu.Game.Screens.Edit
public override bool OnExiting(IScreen next)
{
- if (!exitConfirmed && dialogOverlay != null && HasUnsavedChanges && !(dialogOverlay.CurrentDialog is PromptForSaveDialog))
+ if (!exitConfirmed)
{
- dialogOverlay?.Push(new PromptForSaveDialog(confirmExit, confirmExitWithSave));
- return true;
+ // if the confirm dialog is already showing (or we can't show it, ie. in tests) exit without save.
+ if (dialogOverlay == null || dialogOverlay.CurrentDialog is PromptForSaveDialog)
+ {
+ confirmExit();
+ return true;
+ }
+
+ if (isNewBeatmap || HasUnsavedChanges)
+ {
+ dialogOverlay?.Push(new PromptForSaveDialog(confirmExit, confirmExitWithSave));
+ return true;
+ }
}
Background.FadeColour(Color4.White, 500);
@@ -456,6 +469,12 @@ namespace osu.Game.Screens.Edit
private void confirmExit()
{
+ if (isNewBeatmap)
+ {
+ // confirming exit without save means we should delete the new beatmap completely.
+ beatmapManager.Delete(playableBeatmap.BeatmapInfo.BeatmapSet);
+ }
+
exitConfirmed = true;
this.Exit();
}
diff --git a/osu.Game/Screens/Edit/EditorChangeHandler.cs b/osu.Game/Screens/Edit/EditorChangeHandler.cs
index 616d0608c0..b69e9c4c51 100644
--- a/osu.Game/Screens/Edit/EditorChangeHandler.cs
+++ b/osu.Game/Screens/Edit/EditorChangeHandler.cs
@@ -81,9 +81,6 @@ namespace osu.Game.Screens.Edit
SaveState();
}
- ///
- /// Saves the current state.
- ///
public void SaveState()
{
if (bulkChangesStarted > 0)
diff --git a/osu.Game/Screens/Edit/IEditorChangeHandler.cs b/osu.Game/Screens/Edit/IEditorChangeHandler.cs
index 1774ec6c04..0283421b8c 100644
--- a/osu.Game/Screens/Edit/IEditorChangeHandler.cs
+++ b/osu.Game/Screens/Edit/IEditorChangeHandler.cs
@@ -35,5 +35,11 @@ namespace osu.Game.Screens.Edit
/// This should be invoked as soon as possible after to cause a state change.
///
void EndChange();
+
+ ///
+ /// Immediately saves the current state.
+ /// Note that this will be a no-op if there is a change in progress via .
+ ///
+ void SaveState();
}
}
diff --git a/osu.Game/Screens/Edit/Timing/DifficultySection.cs b/osu.Game/Screens/Edit/Timing/DifficultySection.cs
index 78766d9777..b55d74e3b4 100644
--- a/osu.Game/Screens/Edit/Timing/DifficultySection.cs
+++ b/osu.Game/Screens/Edit/Timing/DifficultySection.cs
@@ -28,6 +28,7 @@ namespace osu.Game.Screens.Edit.Timing
if (point.NewValue != null)
{
multiplierSlider.Current = point.NewValue.SpeedMultiplierBindable;
+ multiplierSlider.Current.BindValueChanged(_ => ChangeHandler?.SaveState());
}
}
diff --git a/osu.Game/Screens/Edit/Timing/EffectSection.cs b/osu.Game/Screens/Edit/Timing/EffectSection.cs
index 71e7f42713..2f143108a9 100644
--- a/osu.Game/Screens/Edit/Timing/EffectSection.cs
+++ b/osu.Game/Screens/Edit/Timing/EffectSection.cs
@@ -28,7 +28,10 @@ namespace osu.Game.Screens.Edit.Timing
if (point.NewValue != null)
{
kiai.Current = point.NewValue.KiaiModeBindable;
+ kiai.Current.BindValueChanged(_ => ChangeHandler?.SaveState());
+
omitBarLine.Current = point.NewValue.OmitFirstBarLineBindable;
+ omitBarLine.Current.BindValueChanged(_ => ChangeHandler?.SaveState());
}
}
diff --git a/osu.Game/Screens/Edit/Timing/SampleSection.cs b/osu.Game/Screens/Edit/Timing/SampleSection.cs
index de986e28ca..280e19c99a 100644
--- a/osu.Game/Screens/Edit/Timing/SampleSection.cs
+++ b/osu.Game/Screens/Edit/Timing/SampleSection.cs
@@ -35,7 +35,10 @@ namespace osu.Game.Screens.Edit.Timing
if (point.NewValue != null)
{
bank.Current = point.NewValue.SampleBankBindable;
+ bank.Current.BindValueChanged(_ => ChangeHandler?.SaveState());
+
volume.Current = point.NewValue.SampleVolumeBindable;
+ volume.Current.BindValueChanged(_ => ChangeHandler?.SaveState());
}
}
diff --git a/osu.Game/Screens/Edit/Timing/Section.cs b/osu.Game/Screens/Edit/Timing/Section.cs
index 603fb77f31..7a81eeb1a4 100644
--- a/osu.Game/Screens/Edit/Timing/Section.cs
+++ b/osu.Game/Screens/Edit/Timing/Section.cs
@@ -32,6 +32,9 @@ namespace osu.Game.Screens.Edit.Timing
[Resolved]
protected Bindable SelectedGroup { get; private set; }
+ [Resolved(canBeNull: true)]
+ protected IEditorChangeHandler ChangeHandler { get; private set; }
+
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
diff --git a/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs b/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs
index 14023b0c35..d5afc8978d 100644
--- a/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs
+++ b/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs
@@ -38,6 +38,7 @@ namespace osu.Game.Screens.Edit.Timing
},
slider = new SettingsSlider
{
+ TransferValueOnCommit = true,
RelativeSizeAxes = Axes.X,
}
}
diff --git a/osu.Game/Screens/Edit/Timing/TimingScreen.cs b/osu.Game/Screens/Edit/Timing/TimingScreen.cs
index 269874fea8..0796097186 100644
--- a/osu.Game/Screens/Edit/Timing/TimingScreen.cs
+++ b/osu.Game/Screens/Edit/Timing/TimingScreen.cs
@@ -81,6 +81,9 @@ namespace osu.Game.Screens.Edit.Timing
[Resolved]
private Bindable selectedGroup { get; set; }
+ [Resolved(canBeNull: true)]
+ private IEditorChangeHandler changeHandler { get; set; }
+
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
@@ -140,6 +143,7 @@ namespace osu.Game.Screens.Edit.Timing
controlGroups.BindCollectionChanged((sender, args) =>
{
table.ControlGroups = controlGroups;
+ changeHandler.SaveState();
}, true);
}
diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs
index 0202441537..2ab8703cc4 100644
--- a/osu.Game/Screens/Edit/Timing/TimingSection.cs
+++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs
@@ -37,8 +37,13 @@ namespace osu.Game.Screens.Edit.Timing
if (point.NewValue != null)
{
bpmSlider.Bindable = point.NewValue.BeatLengthBindable;
+ bpmSlider.Bindable.BindValueChanged(_ => ChangeHandler?.SaveState());
+
bpmTextEntry.Bindable = point.NewValue.BeatLengthBindable;
+ // no need to hook change handler here as it's the same bindable as above
+
timeSignature.Bindable = point.NewValue.TimeSignatureBindable;
+ timeSignature.Bindable.BindValueChanged(_ => ChangeHandler?.SaveState());
}
}
@@ -117,6 +122,8 @@ namespace osu.Game.Screens.Edit.Timing
bpmBindable.BindValueChanged(bpm => beatLengthBindable.Value = beatLengthToBpm(bpm.NewValue));
base.Bindable = bpmBindable;
+
+ TransferValueOnCommit = true;
}
public override Bindable Bindable
diff --git a/osu.Game/Screens/Play/GameplayClock.cs b/osu.Game/Screens/Play/GameplayClock.cs
index 9d04722c12..eeea6777c6 100644
--- a/osu.Game/Screens/Play/GameplayClock.cs
+++ b/osu.Game/Screens/Play/GameplayClock.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Timing;
+using osu.Framework.Utils;
namespace osu.Game.Screens.Play
{
@@ -27,6 +28,8 @@ namespace osu.Game.Screens.Play
///
public virtual IEnumerable> NonGameplayAdjustments => Enumerable.Empty>();
+ private readonly Bindable samplePlaybackDisabled = new Bindable();
+
public GameplayClock(IFrameBasedClock underlyingClock)
{
this.underlyingClock = underlyingClock;
@@ -47,7 +50,12 @@ namespace osu.Game.Screens.Play
double baseRate = Rate;
foreach (var adjustment in NonGameplayAdjustments)
+ {
+ if (Precision.AlmostEquals(adjustment.Value, 0))
+ return 0;
+
baseRate /= adjustment.Value;
+ }
return baseRate;
}
@@ -56,13 +64,15 @@ namespace osu.Game.Screens.Play
public bool IsRunning => underlyingClock.IsRunning;
///
- /// Whether an ongoing seek operation is active.
+ /// Whether nested samples supporting the interface should be paused.
///
- public virtual bool IsSeeking => false;
+ protected virtual bool ShouldDisableSamplePlayback => IsPaused.Value;
public void ProcessFrame()
{
- // we do not want to process the underlying clock.
+ // intentionally not updating the underlying clock (handled externally).
+
+ samplePlaybackDisabled.Value = ShouldDisableSamplePlayback;
}
public double ElapsedFrameTime => underlyingClock.ElapsedFrameTime;
@@ -73,6 +83,6 @@ namespace osu.Game.Screens.Play
public IClock Source => underlyingClock;
- public IBindable SamplePlaybackDisabled => IsPaused;
+ IBindable ISamplePlaybackDisabler.SamplePlaybackDisabled => samplePlaybackDisabled;
}
}
diff --git a/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs b/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs
index 7736541c92..fc4a1a5d83 100644
--- a/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs
+++ b/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs
@@ -13,7 +13,6 @@ using osuTK;
using osuTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Utils;
-using osu.Game.Rulesets.Scoring;
namespace osu.Game.Screens.Play.HUD
{
@@ -106,7 +105,7 @@ namespace osu.Game.Screens.Play.HUD
public void Flash(JudgementResult result)
{
- if (result.Type == HitResult.Miss)
+ if (!result.IsHit)
return;
Scheduler.AddOnce(flash);
diff --git a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs
index 45fdc3ff33..aa2a83774e 100644
--- a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs
+++ b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs
@@ -48,7 +48,7 @@ namespace osu.Game.Screens.Ranking.Statistics
/// The s to display the timing distribution of.
public HitEventTimingDistributionGraph(IReadOnlyList hitEvents)
{
- this.hitEvents = hitEvents.Where(e => !(e.HitObject.HitWindows is HitWindows.EmptyHitWindows) && e.Result != HitResult.Miss).ToList();
+ this.hitEvents = hitEvents.Where(e => !(e.HitObject.HitWindows is HitWindows.EmptyHitWindows) && e.Result.IsHit()).ToList();
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs b/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs
index 18a2238784..055db143d1 100644
--- a/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs
+++ b/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Screens.Ranking.Statistics
public UnstableRate(IEnumerable hitEvents)
: base("Unstable Rate")
{
- var timeOffsets = hitEvents.Where(e => !(e.HitObject.HitWindows is HitWindows.EmptyHitWindows) && e.Result != HitResult.Miss)
+ var timeOffsets = hitEvents.Where(e => !(e.HitObject.HitWindows is HitWindows.EmptyHitWindows) && e.Result.IsHit())
.Select(ev => ev.TimeOffset).ToArray();
Value = 10 * standardDeviation(timeOffsets);
}
diff --git a/osu.Game/Skinning/PausableSkinnableSound.cs b/osu.Game/Skinning/PausableSkinnableSound.cs
index 9819574b1d..d340f67575 100644
--- a/osu.Game/Skinning/PausableSkinnableSound.cs
+++ b/osu.Game/Skinning/PausableSkinnableSound.cs
@@ -34,21 +34,21 @@ namespace osu.Game.Skinning
samplePlaybackDisabled.BindTo(samplePlaybackDisabler.SamplePlaybackDisabled);
samplePlaybackDisabled.BindValueChanged(disabled =>
{
- if (RequestedPlaying)
+ if (!RequestedPlaying) return;
+
+ // let non-looping samples that have already been started play out to completion (sounds better than abruptly cutting off).
+ if (!Looping) return;
+
+ if (disabled.NewValue)
+ base.Stop();
+ else
{
- if (disabled.NewValue)
- base.Stop();
- // it's not easy to know if a sample has finished playing (to end).
- // to keep things simple only resume playing looping samples.
- else if (Looping)
+ // schedule so we don't start playing a sample which is no longer alive.
+ Schedule(() =>
{
- // schedule so we don't start playing a sample which is no longer alive.
- Schedule(() =>
- {
- if (RequestedPlaying)
- base.Play();
- });
- }
+ if (RequestedPlaying)
+ base.Play();
+ });
}
});
}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 3a839ac1a4..fa2135580d 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -24,7 +24,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 31f1af135d..20a51e5feb 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -70,7 +70,7 @@
-
+
@@ -80,7 +80,7 @@
-
+