mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 05:32:54 +08:00
Merge pull request #22291 from ekrctb/revert-result-in-playfield
Move judgement result revert logic to Playfield
This commit is contained in:
commit
b5dda407a8
@ -60,26 +60,24 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
[Test]
|
||||
public void TestCatcherHyperStateReverted()
|
||||
{
|
||||
DrawableCatchHitObject drawableObject1 = null;
|
||||
DrawableCatchHitObject drawableObject2 = null;
|
||||
JudgementResult result1 = null;
|
||||
JudgementResult result2 = null;
|
||||
AddStep("catch hyper fruit", () =>
|
||||
{
|
||||
attemptCatch(new Fruit { HyperDashTarget = new Fruit { X = 100 } }, out drawableObject1, out result1);
|
||||
result1 = attemptCatch(new Fruit { HyperDashTarget = new Fruit { X = 100 } });
|
||||
});
|
||||
AddStep("catch normal fruit", () =>
|
||||
{
|
||||
attemptCatch(new Fruit(), out drawableObject2, out result2);
|
||||
result2 = attemptCatch(new Fruit());
|
||||
});
|
||||
AddStep("revert second result", () =>
|
||||
{
|
||||
catcher.OnRevertResult(drawableObject2, result2);
|
||||
catcher.OnRevertResult(result2);
|
||||
});
|
||||
checkHyperDash(true);
|
||||
AddStep("revert first result", () =>
|
||||
{
|
||||
catcher.OnRevertResult(drawableObject1, result1);
|
||||
catcher.OnRevertResult(result1);
|
||||
});
|
||||
checkHyperDash(false);
|
||||
}
|
||||
@ -87,16 +85,15 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
[Test]
|
||||
public void TestCatcherAnimationStateReverted()
|
||||
{
|
||||
DrawableCatchHitObject drawableObject = null;
|
||||
JudgementResult result = null;
|
||||
AddStep("catch kiai fruit", () =>
|
||||
{
|
||||
attemptCatch(new TestKiaiFruit(), out drawableObject, out result);
|
||||
result = attemptCatch(new TestKiaiFruit());
|
||||
});
|
||||
checkState(CatcherAnimationState.Kiai);
|
||||
AddStep("revert result", () =>
|
||||
{
|
||||
catcher.OnRevertResult(drawableObject, result);
|
||||
catcher.OnRevertResult(result);
|
||||
});
|
||||
checkState(CatcherAnimationState.Idle);
|
||||
}
|
||||
@ -268,23 +265,19 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
|
||||
private void checkHyperDash(bool state) => AddAssert($"catcher is {(state ? "" : "not ")}hyper dashing", () => catcher.HyperDashing == state);
|
||||
|
||||
private void attemptCatch(CatchHitObject hitObject)
|
||||
{
|
||||
attemptCatch(() => hitObject, 1);
|
||||
}
|
||||
|
||||
private void attemptCatch(Func<CatchHitObject> hitObject, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
attemptCatch(hitObject(), out _, out _);
|
||||
attemptCatch(hitObject());
|
||||
}
|
||||
|
||||
private void attemptCatch(CatchHitObject hitObject, out DrawableCatchHitObject drawableObject, out JudgementResult result)
|
||||
private JudgementResult attemptCatch(CatchHitObject hitObject)
|
||||
{
|
||||
hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
drawableObject = createDrawableObject(hitObject);
|
||||
result = createResult(hitObject);
|
||||
var drawableObject = createDrawableObject(hitObject);
|
||||
var result = createResult(hitObject);
|
||||
applyResult(drawableObject, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void applyResult(DrawableCatchHitObject drawableObject, JudgementResult result)
|
||||
|
@ -63,12 +63,12 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
updateCombo(result.ComboAtJudgement + 1, judgedObject.AccentColour.Value);
|
||||
}
|
||||
|
||||
public void OnRevertResult(DrawableCatchHitObject judgedObject, JudgementResult result)
|
||||
public void OnRevertResult(JudgementResult result)
|
||||
{
|
||||
if (!result.Type.AffectsCombo() || !result.HasResult)
|
||||
return;
|
||||
|
||||
updateCombo(result.ComboAtJudgement, judgedObject.AccentColour.Value);
|
||||
updateCombo(result.ComboAtJudgement, null);
|
||||
}
|
||||
|
||||
private void updateCombo(int newCombo, Color4? hitObjectColour)
|
||||
|
@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||
=> CatcherArea.OnNewResult((DrawableCatchHitObject)judgedObject, result);
|
||||
|
||||
private void onRevertResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||
=> CatcherArea.OnRevertResult((DrawableCatchHitObject)judgedObject, result);
|
||||
private void onRevertResult(JudgementResult result)
|
||||
=> CatcherArea.OnRevertResult(result);
|
||||
}
|
||||
}
|
||||
|
@ -254,7 +254,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
}
|
||||
}
|
||||
|
||||
public void OnRevertResult(DrawableCatchHitObject drawableObject, JudgementResult result)
|
||||
public void OnRevertResult(JudgementResult result)
|
||||
{
|
||||
var catchResult = (CatchJudgementResult)result;
|
||||
|
||||
@ -268,8 +268,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
SetHyperDashState();
|
||||
}
|
||||
|
||||
caughtObjectContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject, false);
|
||||
droppedObjectTarget.RemoveAll(d => d.HitObject == drawableObject.HitObject, false);
|
||||
caughtObjectContainer.RemoveAll(d => d.HitObject == result.HitObject, false);
|
||||
droppedObjectTarget.RemoveAll(d => d.HitObject == result.HitObject, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -73,10 +73,10 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
comboDisplay.OnNewResult(hitObject, result);
|
||||
}
|
||||
|
||||
public void OnRevertResult(DrawableCatchHitObject hitObject, JudgementResult result)
|
||||
public void OnRevertResult(JudgementResult result)
|
||||
{
|
||||
comboDisplay.OnRevertResult(hitObject, result);
|
||||
Catcher.OnRevertResult(hitObject, result);
|
||||
comboDisplay.OnRevertResult(result);
|
||||
Catcher.OnRevertResult(result);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
|
@ -19,7 +19,6 @@ using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@ -37,6 +36,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private TestDrawablePoolingRuleset drawableRuleset;
|
||||
|
||||
private TestPlayfield playfield => (TestPlayfield)drawableRuleset.Playfield;
|
||||
|
||||
[Test]
|
||||
public void TestReusedWithHitObjectsSpacedFarApart()
|
||||
{
|
||||
@ -133,29 +134,49 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddUntilStep("no DHOs shown", () => !this.ChildrenOfType<DrawableTestHitObject>().Any());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRevertResult()
|
||||
{
|
||||
ManualClock clock = null;
|
||||
Beatmap beatmap;
|
||||
|
||||
createTest(beatmap = new Beatmap
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new TestHitObject { StartTime = 0 },
|
||||
new TestHitObject { StartTime = 500 },
|
||||
new TestHitObject { StartTime = 1000 },
|
||||
}
|
||||
}, 10, () => new FramedClock(clock = new ManualClock()));
|
||||
|
||||
AddStep("fast forward to end", () => clock.CurrentTime = beatmap.HitObjects[^1].GetEndTime() + 100);
|
||||
AddUntilStep("all judged", () => playfield.JudgedObjects.Count, () => Is.EqualTo(3));
|
||||
|
||||
AddStep("rewind to middle", () => clock.CurrentTime = beatmap.HitObjects[1].StartTime - 100);
|
||||
AddUntilStep("some results reverted", () => playfield.JudgedObjects.Count, () => Is.EqualTo(1));
|
||||
|
||||
AddStep("fast forward to end", () => clock.CurrentTime = beatmap.HitObjects[^1].GetEndTime() + 100);
|
||||
AddUntilStep("all judged", () => playfield.JudgedObjects.Count, () => Is.EqualTo(3));
|
||||
|
||||
AddStep("disable frame stability", () => drawableRuleset.FrameStablePlayback = false);
|
||||
AddStep("instant seek to start", () => clock.CurrentTime = beatmap.HitObjects[0].StartTime - 100);
|
||||
AddAssert("all results reverted", () => playfield.JudgedObjects.Count, () => Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestApplyHitResultOnKilled()
|
||||
{
|
||||
ManualClock clock = null;
|
||||
bool anyJudged = false;
|
||||
|
||||
void onNewResult(JudgementResult _) => anyJudged = true;
|
||||
|
||||
var beatmap = new Beatmap();
|
||||
beatmap.HitObjects.Add(new TestKilledHitObject { Duration = 20 });
|
||||
|
||||
createTest(beatmap, 10, () => new FramedClock(clock = new ManualClock()));
|
||||
|
||||
AddStep("subscribe to new result", () =>
|
||||
{
|
||||
anyJudged = false;
|
||||
drawableRuleset.NewResult += onNewResult;
|
||||
});
|
||||
AddStep("skip past object", () => clock.CurrentTime = beatmap.HitObjects[0].GetEndTime() + 1000);
|
||||
|
||||
AddAssert("object judged", () => anyJudged);
|
||||
|
||||
AddStep("clean up", () => drawableRuleset.NewResult -= onNewResult);
|
||||
AddAssert("object judged", () => playfield.JudgedObjects.Count == 1);
|
||||
}
|
||||
|
||||
private void createTest(IBeatmap beatmap, int poolSize, Func<IFrameBasedClock> createClock = null)
|
||||
@ -212,12 +233,24 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private partial class TestPlayfield : Playfield
|
||||
{
|
||||
public readonly HashSet<HitObject> JudgedObjects = new HashSet<HitObject>();
|
||||
|
||||
private readonly int poolSize;
|
||||
|
||||
public TestPlayfield(int poolSize)
|
||||
{
|
||||
this.poolSize = poolSize;
|
||||
AddInternal(HitObjectContainer);
|
||||
NewResult += (_, r) =>
|
||||
{
|
||||
Assert.That(JudgedObjects, Has.No.Member(r.HitObject));
|
||||
JudgedObjects.Add(r.HitObject);
|
||||
};
|
||||
RevertResult += r =>
|
||||
{
|
||||
Assert.That(JudgedObjects, Has.Member(r.HitObject));
|
||||
JudgedObjects.Remove(r.HitObject);
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
@ -22,12 +22,18 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Cached(typeof(ScoreProcessor))]
|
||||
private TestScoreProcessor scoreProcessor = new TestScoreProcessor();
|
||||
|
||||
private readonly OsuHitWindows hitWindows = new OsuHitWindows();
|
||||
private readonly OsuHitWindows hitWindows;
|
||||
|
||||
private UnstableRateCounter counter;
|
||||
|
||||
private double prev;
|
||||
|
||||
public TestSceneUnstableRateCounter()
|
||||
{
|
||||
hitWindows = new OsuHitWindows();
|
||||
hitWindows.SetDifficulty(5);
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUp()
|
||||
{
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@ -33,16 +34,30 @@ namespace osu.Game.Rulesets.Judgements
|
||||
public readonly Judgement Judgement;
|
||||
|
||||
/// <summary>
|
||||
/// The offset from a perfect hit at which this <see cref="JudgementResult"/> occurred.
|
||||
/// The time at which this <see cref="JudgementResult"/> occurred.
|
||||
/// Populated when this <see cref="JudgementResult"/> is applied via <see cref="DrawableHitObject.ApplyResult"/>.
|
||||
/// </summary>
|
||||
public double TimeOffset { get; internal set; }
|
||||
/// <remarks>
|
||||
/// This is used instead of <see cref="TimeAbsolute"/> to check whether this <see cref="JudgementResult"/> should be reverted.
|
||||
/// </remarks>
|
||||
internal double? RawTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The absolute time at which this <see cref="JudgementResult"/> occurred.
|
||||
/// Equal to the (end) time of the <see cref="HitObject"/> + <see cref="TimeOffset"/>.
|
||||
/// The offset of <see cref="TimeAbsolute"/> from the end time of <see cref="HitObject"/>, clamped by <see cref="osu.Game.Rulesets.Objects.HitObject.MaximumJudgementOffset"/>.
|
||||
/// </summary>
|
||||
public double TimeAbsolute => HitObject.GetEndTime() + TimeOffset;
|
||||
public double TimeOffset
|
||||
{
|
||||
get => RawTime != null ? Math.Min(RawTime.Value - HitObject.GetEndTime(), HitObject.MaximumJudgementOffset) : 0;
|
||||
internal set => RawTime = HitObject.GetEndTime() + value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The absolute time at which this <see cref="JudgementResult"/> occurred, clamped by the end time of <see cref="HitObject"/> plus <see cref="osu.Game.Rulesets.Objects.HitObject.MaximumJudgementOffset"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The end time of <see cref="HitObject"/> is returned if this result is not populated yet.
|
||||
/// </remarks>
|
||||
public double TimeAbsolute => RawTime != null ? Math.Min(RawTime.Value, HitObject.GetEndTime() + HitObject.MaximumJudgementOffset) : HitObject.GetEndTime();
|
||||
|
||||
/// <summary>
|
||||
/// The combo prior to this <see cref="JudgementResult"/> occurring.
|
||||
@ -83,6 +98,13 @@ namespace osu.Game.Rulesets.Judgements
|
||||
{
|
||||
HitObject = hitObject;
|
||||
Judgement = judgement;
|
||||
Reset();
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
Type = HitResult.None;
|
||||
RawTime = null;
|
||||
}
|
||||
|
||||
public override string ToString() => $"{Type} (Score:{Judgement.NumericResultFor(this)} HP:{Judgement.HealthIncreaseFor(this)} {Judgement})";
|
||||
|
@ -82,6 +82,9 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// <summary>
|
||||
/// Invoked by this or a nested <see cref="DrawableHitObject"/> prior to a <see cref="JudgementResult"/> being reverted.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only invoked if this <see cref="DrawableHitObject"/> is alive when the result is reverted.
|
||||
/// </remarks>
|
||||
public event Action<DrawableHitObject, JudgementResult> OnRevertResult;
|
||||
|
||||
/// <summary>
|
||||
@ -222,6 +225,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
|
||||
ensureEntryHasResult();
|
||||
|
||||
entry.RevertResult += onRevertResult;
|
||||
|
||||
foreach (var h in HitObject.NestedHitObjects)
|
||||
{
|
||||
var pooledDrawableNested = pooledObjectProvider?.GetPooledDrawableRepresentation(h, this);
|
||||
@ -234,7 +239,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
OnNestedDrawableCreated?.Invoke(drawableNested);
|
||||
|
||||
drawableNested.OnNewResult += onNewResult;
|
||||
drawableNested.OnRevertResult += onRevertResult;
|
||||
drawableNested.ApplyCustomUpdateState += onApplyCustomUpdateState;
|
||||
|
||||
// This is only necessary for non-pooled DHOs. For pooled DHOs, this is handled inside GetPooledDrawableRepresentation().
|
||||
@ -308,7 +312,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
foreach (var obj in nestedHitObjects)
|
||||
{
|
||||
obj.OnNewResult -= onNewResult;
|
||||
obj.OnRevertResult -= onRevertResult;
|
||||
obj.ApplyCustomUpdateState -= onApplyCustomUpdateState;
|
||||
}
|
||||
|
||||
@ -317,6 +320,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
|
||||
HitObject.DefaultsApplied -= onDefaultsApplied;
|
||||
|
||||
entry.RevertResult -= onRevertResult;
|
||||
|
||||
OnFree();
|
||||
|
||||
ParentHitObject = null;
|
||||
@ -365,7 +370,11 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
|
||||
private void onNewResult(DrawableHitObject drawableHitObject, JudgementResult result) => OnNewResult?.Invoke(drawableHitObject, result);
|
||||
|
||||
private void onRevertResult(DrawableHitObject drawableHitObject, JudgementResult result) => OnRevertResult?.Invoke(drawableHitObject, result);
|
||||
private void onRevertResult()
|
||||
{
|
||||
updateState(ArmedState.Idle);
|
||||
OnRevertResult?.Invoke(this, Result);
|
||||
}
|
||||
|
||||
private void onApplyCustomUpdateState(DrawableHitObject drawableHitObject, ArmedState state) => ApplyCustomUpdateState?.Invoke(drawableHitObject, state);
|
||||
|
||||
@ -577,26 +586,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
|
||||
#endregion
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (Result != null && Result.HasResult)
|
||||
{
|
||||
double endTime = HitObject.GetEndTime();
|
||||
|
||||
if (Result.TimeOffset + endTime > Time.Current)
|
||||
{
|
||||
OnRevertResult?.Invoke(this, Result);
|
||||
|
||||
Result.TimeOffset = 0;
|
||||
Result.Type = HitResult.None;
|
||||
|
||||
updateState(ArmedState.Idle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) => false;
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
@ -671,7 +660,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);
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Performance;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
@ -26,6 +27,8 @@ namespace osu.Game.Rulesets.Objects
|
||||
|
||||
private readonly IBindable<double> startTimeBindable = new BindableDouble();
|
||||
|
||||
internal event Action? RevertResult;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="HitObjectLifetimeEntry"/>.
|
||||
/// </summary>
|
||||
@ -95,5 +98,7 @@ namespace osu.Game.Rulesets.Objects
|
||||
/// Set <see cref="LifetimeEntry.LifetimeStart"/> using <see cref="InitialLifetimeOffset"/>.
|
||||
/// </summary>
|
||||
internal void SetInitialLifetime() => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset;
|
||||
|
||||
internal void OnRevertResult() => RevertResult?.Invoke();
|
||||
}
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.UI
|
||||
playfield = new Lazy<Playfield>(() => CreatePlayfield().With(p =>
|
||||
{
|
||||
p.NewResult += (_, r) => NewResult?.Invoke(r);
|
||||
p.RevertResult += (_, r) => RevertResult?.Invoke(r);
|
||||
p.RevertResult += r => RevertResult?.Invoke(r);
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -28,11 +28,6 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
public event Action<DrawableHitObject, JudgementResult> NewResult;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a <see cref="DrawableHitObject"/> judgement is reverted.
|
||||
/// </summary>
|
||||
public event Action<DrawableHitObject, JudgementResult> RevertResult;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a <see cref="HitObject"/> becomes used by a <see cref="DrawableHitObject"/>.
|
||||
/// </summary>
|
||||
@ -111,7 +106,6 @@ namespace osu.Game.Rulesets.UI
|
||||
private void addDrawable(DrawableHitObject drawable)
|
||||
{
|
||||
drawable.OnNewResult += onNewResult;
|
||||
drawable.OnRevertResult += onRevertResult;
|
||||
|
||||
bindStartTime(drawable);
|
||||
AddInternal(drawable);
|
||||
@ -120,7 +114,6 @@ namespace osu.Game.Rulesets.UI
|
||||
private void removeDrawable(DrawableHitObject drawable)
|
||||
{
|
||||
drawable.OnNewResult -= onNewResult;
|
||||
drawable.OnRevertResult -= onRevertResult;
|
||||
|
||||
unbindStartTime(drawable);
|
||||
|
||||
@ -154,7 +147,6 @@ namespace osu.Game.Rulesets.UI
|
||||
#endregion
|
||||
|
||||
private void onNewResult(DrawableHitObject d, JudgementResult r) => NewResult?.Invoke(d, r);
|
||||
private void onRevertResult(DrawableHitObject d, JudgementResult r) => RevertResult?.Invoke(d, r);
|
||||
|
||||
#region Comparator + StartTime tracking
|
||||
|
||||
|
@ -22,6 +22,7 @@ using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osu.Game.Rulesets.Objects.Pooling;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
|
||||
namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
@ -35,9 +36,9 @@ namespace osu.Game.Rulesets.UI
|
||||
public event Action<DrawableHitObject, JudgementResult> NewResult;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a <see cref="DrawableHitObject"/> judgement is reverted.
|
||||
/// Invoked when a judgement result is reverted.
|
||||
/// </summary>
|
||||
public event Action<DrawableHitObject, JudgementResult> RevertResult;
|
||||
public event Action<JudgementResult> RevertResult;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="DrawableHitObject"/> contained in this Playfield.
|
||||
@ -98,6 +99,8 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
private readonly HitObjectEntryManager entryManager = new HitObjectEntryManager();
|
||||
|
||||
private readonly Stack<HitObjectLifetimeEntry> judgedEntries;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Playfield"/>.
|
||||
/// </summary>
|
||||
@ -107,14 +110,15 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
hitObjectContainerLazy = new Lazy<HitObjectContainer>(() => CreateHitObjectContainer().With(h =>
|
||||
{
|
||||
h.NewResult += (d, r) => NewResult?.Invoke(d, r);
|
||||
h.RevertResult += (d, r) => RevertResult?.Invoke(d, r);
|
||||
h.NewResult += onNewResult;
|
||||
h.HitObjectUsageBegan += o => HitObjectUsageBegan?.Invoke(o);
|
||||
h.HitObjectUsageFinished += o => HitObjectUsageFinished?.Invoke(o);
|
||||
}));
|
||||
|
||||
entryManager.OnEntryAdded += onEntryAdded;
|
||||
entryManager.OnEntryRemoved += onEntryRemoved;
|
||||
|
||||
judgedEntries = new Stack<HitObjectLifetimeEntry>();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -224,7 +228,7 @@ namespace osu.Game.Rulesets.UI
|
||||
otherPlayfield.DisplayJudgements.BindTo(DisplayJudgements);
|
||||
|
||||
otherPlayfield.NewResult += (d, r) => NewResult?.Invoke(d, r);
|
||||
otherPlayfield.RevertResult += (d, r) => RevertResult?.Invoke(d, r);
|
||||
otherPlayfield.RevertResult += r => RevertResult?.Invoke(r);
|
||||
otherPlayfield.HitObjectUsageBegan += h => HitObjectUsageBegan?.Invoke(h);
|
||||
otherPlayfield.HitObjectUsageFinished += h => HitObjectUsageFinished?.Invoke(h);
|
||||
|
||||
@ -252,6 +256,18 @@ namespace osu.Game.Rulesets.UI
|
||||
updatable.Update(this);
|
||||
}
|
||||
}
|
||||
|
||||
// When rewinding, revert future judgements in the reverse order.
|
||||
while (judgedEntries.Count > 0)
|
||||
{
|
||||
var result = judgedEntries.Peek().Result;
|
||||
Debug.Assert(result?.RawTime != null);
|
||||
|
||||
if (Time.Current >= result.RawTime.Value)
|
||||
break;
|
||||
|
||||
revertResult(judgedEntries.Pop());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -443,6 +459,25 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
#endregion
|
||||
|
||||
private void onNewResult(DrawableHitObject drawable, JudgementResult result)
|
||||
{
|
||||
Debug.Assert(result != null && drawable.Entry?.Result == result && result.RawTime != null);
|
||||
judgedEntries.Push(drawable.Entry.AsNonNull());
|
||||
|
||||
NewResult?.Invoke(drawable, result);
|
||||
}
|
||||
|
||||
private void revertResult(HitObjectLifetimeEntry entry)
|
||||
{
|
||||
var result = entry.Result;
|
||||
Debug.Assert(result != null);
|
||||
|
||||
RevertResult?.Invoke(result);
|
||||
entry.OnRevertResult();
|
||||
|
||||
result.Reset();
|
||||
}
|
||||
|
||||
#region Editor logic
|
||||
|
||||
/// <summary>
|
||||
|
Loading…
Reference in New Issue
Block a user