diff --git a/osu.Game/Rulesets/Judgements/JudgementResult.cs b/osu.Game/Rulesets/Judgements/JudgementResult.cs
index 6749fd7932..bf29919e34 100644
--- a/osu.Game/Rulesets/Judgements/JudgementResult.cs
+++ b/osu.Game/Rulesets/Judgements/JudgementResult.cs
@@ -3,6 +3,7 @@
#nullable disable
+using System;
using JetBrains.Annotations;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
@@ -33,19 +34,30 @@ namespace osu.Game.Rulesets.Judgements
public readonly Judgement Judgement;
///
- /// The offset of from the end time of , clamped by .
- /// Populated when this is applied via .
- ///
- public double TimeOffset { get; internal set; }
-
- ///
- /// The absolute time at which this occurred.
+ /// The time at which this occurred.
/// Populated when this is applied via .
///
///
- /// This is initially set to the end time of .
+ /// This is used instead of to check whether this should be reverted.
///
- public double TimeAbsolute { get; internal set; }
+ internal double? RawTime { get; set; }
+
+ ///
+ /// The offset of from the end time of , clamped by .
+ ///
+ public double TimeOffset
+ {
+ get => RawTime != null ? Math.Min(RawTime.Value - HitObject.GetEndTime(), HitObject.MaximumJudgementOffset) : 0;
+ internal set => RawTime = HitObject.GetEndTime() + value;
+ }
+
+ ///
+ /// The absolute time at which this occurred, clamped by the end time of plus .
+ ///
+ ///
+ /// The end time of is returned if this result is not populated yet.
+ ///
+ public double TimeAbsolute => RawTime != null ? Math.Min(RawTime.Value, HitObject.GetEndTime() + HitObject.MaximumJudgementOffset) : HitObject.GetEndTime();
///
/// The combo prior to this occurring.
@@ -92,8 +104,7 @@ namespace osu.Game.Rulesets.Judgements
internal void Reset()
{
Type = HitResult.None;
- TimeOffset = 0;
- TimeAbsolute = HitObject.GetEndTime();
+ RawTime = null;
}
public override string ToString() => $"{Type} (Score:{Judgement.NumericResultFor(this)} HP:{Judgement.HealthIncreaseFor(this)} {Judgement})";
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index d30fc00bfc..f7c6340419 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -661,7 +661,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
$"{GetType().ReadableName()} applied an invalid hit result (was: {Result.Type}, expected: [{Result.Judgement.MinResult} ... {Result.Judgement.MaxResult}]).");
}
- Result.TimeOffset = Math.Min(HitObject.MaximumJudgementOffset, Time.Current - HitObject.GetEndTime());
+ Result.RawTime = Time.Current;
if (Result.HasResult)
updateState(Result.IsHit ? ArmedState.Hit : ArmedState.Miss);
diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs
index 5d6a8de33c..b1c3b78e67 100644
--- a/osu.Game/Rulesets/UI/Playfield.cs
+++ b/osu.Game/Rulesets/UI/Playfield.cs
@@ -258,8 +258,16 @@ namespace osu.Game.Rulesets.UI
}
// When rewinding, revert future judgements in the reverse order.
- while (judgedEntries.Count > 0 && Time.Current < judgedEntries.Peek().Result.AsNonNull().TimeAbsolute)
+ while (judgedEntries.Count > 0)
+ {
+ var result = judgedEntries.Peek().Result;
+ Debug.Assert(result?.RawTime != null);
+
+ if (Time.Current >= result.RawTime.Value)
+ break;
+
revertResult(judgedEntries.Pop());
+ }
}
///
@@ -453,7 +461,7 @@ namespace osu.Game.Rulesets.UI
private void onNewResult(DrawableHitObject drawable, JudgementResult result)
{
- Debug.Assert(result != null && drawable.Entry?.Result == result);
+ Debug.Assert(result != null && drawable.Entry?.Result == result && result.RawTime != null);
judgedEntries.Push(drawable.Entry.AsNonNull());
NewResult?.Invoke(drawable, result);