From d2775680e66129c34297246ce047914d2b94fd90 Mon Sep 17 00:00:00 2001 From: Chandler Stowell Date: Wed, 24 Jan 2024 13:13:45 -0500 Subject: [PATCH 1/8] use stack to pass action state when applying hit results this removes closure allocations --- .../Drawables/DrawableEmptyFreeformHitObject.cs | 2 +- .../Drawables/DrawablePippidonHitObject.cs | 7 ++++++- .../Drawables/DrawableEmptyScrollingHitObject.cs | 2 +- .../Drawables/DrawablePippidonHitObject.cs | 7 ++++++- .../Objects/Drawables/DrawableCatchHitObject.cs | 7 ++++++- .../Objects/Drawables/DrawableHoldNote.cs | 2 +- .../Objects/Drawables/DrawableHoldNoteBody.cs | 2 +- .../Objects/Drawables/DrawableManiaHitObject.cs | 2 +- .../Objects/Drawables/DrawableNote.cs | 4 ++-- .../Objects/Drawables/DrawableHitCircle.cs | 15 ++++++++------- .../Objects/Drawables/DrawableOsuHitObject.cs | 4 ++-- .../Objects/Drawables/DrawableSlider.cs | 13 ++++++++----- .../Objects/Drawables/DrawableSpinner.cs | 12 ++++++------ .../Objects/Drawables/DrawableSpinnerTick.cs | 2 +- .../Objects/Drawables/DrawableDrumRoll.cs | 7 +++++-- .../Objects/Drawables/DrawableDrumRollTick.cs | 11 +++++++---- .../Objects/Drawables/DrawableFlyingHit.cs | 2 +- .../Objects/Drawables/DrawableHit.cs | 12 ++++++------ .../Objects/Drawables/DrawableStrongNestedHit.cs | 2 +- .../Objects/Drawables/DrawableSwell.cs | 8 ++++++-- .../Objects/Drawables/DrawableSwellTick.cs | 5 ++++- .../Objects/Drawables/DrawableHitObject.cs | 13 +++++++++++-- 22 files changed, 91 insertions(+), 50 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Objects/Drawables/DrawableEmptyFreeformHitObject.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Objects/Drawables/DrawableEmptyFreeformHitObject.cs index 744e207b57..e8f511bc4b 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Objects/Drawables/DrawableEmptyFreeformHitObject.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Objects/Drawables/DrawableEmptyFreeformHitObject.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.EmptyFreeform.Objects.Drawables { if (timeOffset >= 0) // todo: implement judgement logic - ApplyResult(r => r.Type = HitResult.Perfect); + ApplyResult(static r => r.Type = HitResult.Perfect); } protected override void UpdateHitStateTransforms(ArmedState state) diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs index c5ada4288d..a8bb57ba18 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs @@ -49,7 +49,12 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables protected override void CheckForResult(bool userTriggered, double timeOffset) { if (timeOffset >= 0) - ApplyResult(r => r.Type = IsHovered ? HitResult.Perfect : HitResult.Miss); + { + ApplyResult(static (r, isHovered) => + { + r.Type = isHovered ? HitResult.Perfect : HitResult.Miss; + }, IsHovered); + } } protected override double InitialLifetimeOffset => time_preempt; diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Objects/Drawables/DrawableEmptyScrollingHitObject.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Objects/Drawables/DrawableEmptyScrollingHitObject.cs index a3c3b89105..070a802aea 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Objects/Drawables/DrawableEmptyScrollingHitObject.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Objects/Drawables/DrawableEmptyScrollingHitObject.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.EmptyScrolling.Objects.Drawables { if (timeOffset >= 0) // todo: implement judgement logic - ApplyResult(r => r.Type = HitResult.Perfect); + ApplyResult(static r => r.Type = HitResult.Perfect); } protected override void UpdateHitStateTransforms(ArmedState state) diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs index d198fa81cb..9983ec20b0 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs @@ -49,7 +49,12 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables protected override void CheckForResult(bool userTriggered, double timeOffset) { if (timeOffset >= 0) - ApplyResult(r => r.Type = currentLane.Value == HitObject.Lane ? HitResult.Perfect : HitResult.Miss); + { + ApplyResult(static (r, pippidonHitObject) => + { + r.Type = pippidonHitObject.currentLane.Value == pippidonHitObject.HitObject.Lane ? HitResult.Perfect : HitResult.Miss; + }, this); + } } protected override void UpdateHitStateTransforms(ArmedState state) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs index 7f8c17861d..5a921f36f5 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs @@ -63,7 +63,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables if (CheckPosition == null) return; if (timeOffset >= 0 && Result != null) - ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? r.Judgement.MaxResult : r.Judgement.MinResult); + { + ApplyResult(static (r, state) => + { + r.Type = state.CheckPosition.Invoke(state.HitObject) ? r.Judgement.MaxResult : r.Judgement.MinResult; + }, this); + } } protected override void UpdateHitStateTransforms(ArmedState state) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 3490d50871..e5056d5167 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -265,7 +265,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (Tail.AllJudged) { if (Tail.IsHit) - ApplyResult(r => r.Type = r.Judgement.MaxResult); + ApplyResult(static r => r.Type = r.Judgement.MaxResult); else MissForcefully(); } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteBody.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteBody.cs index 1b2efbafdf..317da0580c 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteBody.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteBody.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { if (AllJudged) return; - ApplyResult(r => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult); + ApplyResult(static (r, hit) => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult, hit); } } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 8498fd36de..dea0817869 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables /// /// Causes this to get missed, disregarding all conditions in implementations of . /// - public virtual void MissForcefully() => ApplyResult(r => r.Type = r.Judgement.MinResult); + public virtual void MissForcefully() => ApplyResult(static r => r.Type = r.Judgement.MinResult); } public abstract partial class DrawableManiaHitObject : DrawableManiaHitObject diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 680009bc4c..985007f905 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -89,7 +89,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (!userTriggered) { if (!HitObject.HitWindows.CanBeHit(timeOffset)) - ApplyResult(r => r.Type = r.Judgement.MinResult); + ApplyResult(static r => r.Type = r.Judgement.MinResult); return; } @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables result = GetCappedResult(result); - ApplyResult(r => r.Type = result); + ApplyResult(static (r, result) => r.Type = result, result); } /// diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 0d665cad0c..8284229d82 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -155,7 +155,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (!userTriggered) { if (!HitObject.HitWindows.CanBeHit(timeOffset)) - ApplyResult(r => r.Type = r.Judgement.MinResult); + ApplyResult(static r => r.Type = r.Judgement.MinResult); return; } @@ -169,19 +169,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (result == HitResult.None || clickAction != ClickAction.Hit) return; - ApplyResult(r => + ApplyResult(static (r, state) => { + var (hitCircle, hitResult) = state; 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.IsHit()) + if (hitResult.IsHit()) { - var localMousePosition = ToLocalSpace(inputManager.CurrentState.Mouse.Position); - circleResult.CursorPositionAtHit = HitObject.StackedPosition + (localMousePosition - DrawSize / 2); + var localMousePosition = hitCircle.ToLocalSpace(hitCircle.inputManager.CurrentState.Mouse.Position); + circleResult.CursorPositionAtHit = hitCircle.HitObject.StackedPosition + (localMousePosition - hitCircle.DrawSize / 2); } - circleResult.Type = result; - }); + circleResult.Type = hitResult; + }, (this, result)); } /// diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 5b379a0d90..cc06d009c9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -100,12 +100,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// /// Causes this to get hit, disregarding all conditions in implementations of . /// - public void HitForcefully() => ApplyResult(r => r.Type = r.Judgement.MaxResult); + public void HitForcefully() => ApplyResult(static r => r.Type = r.Judgement.MaxResult); /// /// Causes this to get missed, disregarding all conditions in implementations of . /// - public void MissForcefully() => ApplyResult(r => r.Type = r.Judgement.MinResult); + public void MissForcefully() => ApplyResult(static r => r.Type = r.Judgement.MinResult); private RectangleF parentScreenSpaceRectangle => ((DrawableOsuHitObject)ParentHitObject)?.parentScreenSpaceRectangle ?? Parent!.ScreenSpaceDrawQuad.AABBFloat; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index baec200107..3c298cc6af 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -292,10 +292,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (HitObject.ClassicSliderBehaviour) { // Classic behaviour means a slider is judged proportionally to the number of nested hitobjects hit. This is the classic osu!stable scoring. - ApplyResult(r => + ApplyResult(static (r, nestedHitObjects) => { - int totalTicks = NestedHitObjects.Count; - int hitTicks = NestedHitObjects.Count(h => h.IsHit); + int totalTicks = nestedHitObjects.Count; + int hitTicks = nestedHitObjects.Count(h => h.IsHit); if (hitTicks == totalTicks) r.Type = HitResult.Great; @@ -306,13 +306,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables double hitFraction = (double)hitTicks / totalTicks; r.Type = hitFraction >= 0.5 ? HitResult.Ok : HitResult.Meh; } - }); + }, NestedHitObjects); } else { // If only the nested hitobjects are judged, then the slider's own judgement is ignored for scoring purposes. // But the slider needs to still be judged with a reasonable hit/miss result for visual purposes (hit/miss transforms, etc). - ApplyResult(r => r.Type = NestedHitObjects.Any(h => h.Result.IsHit) ? r.Judgement.MaxResult : r.Judgement.MinResult); + ApplyResult(static (r, nestedHitObjects) => + { + r.Type = nestedHitObjects.Any(h => h.Result.IsHit) ? r.Judgement.MaxResult : r.Judgement.MinResult; + }, NestedHitObjects); } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index bf4b07eaab..d21d02c8ce 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -258,17 +258,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables foreach (var tick in ticks.Where(t => !t.Result.HasResult)) tick.TriggerResult(false); - ApplyResult(r => + ApplyResult(static (r, spinner) => { - if (Progress >= 1) + if (spinner.Progress >= 1) r.Type = HitResult.Great; - else if (Progress > .9) + else if (spinner.Progress > .9) r.Type = HitResult.Ok; - else if (Progress > .75) + else if (spinner.Progress > .75) r.Type = HitResult.Meh; - else if (Time.Current >= HitObject.EndTime) + else if (spinner.Time.Current >= spinner.HitObject.EndTime) r.Type = r.Judgement.MinResult; - }); + }, this); } protected override void Update() diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs index 5b55533edd..1c3ff29118 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs @@ -35,6 +35,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// Apply a judgement result. /// /// Whether this tick was reached. - internal void TriggerResult(bool hit) => ApplyResult(r => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult); + internal void TriggerResult(bool hit) => ApplyResult(static (r, hit) => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult, hit); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 2bf0c04adf..d3fe363857 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (timeOffset < 0) return; - ApplyResult(r => r.Type = r.Judgement.MaxResult); + ApplyResult(static r => r.Type = r.Judgement.MaxResult); } protected override void UpdateHitStateTransforms(ArmedState state) @@ -192,7 +192,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (!ParentHitObject.Judged) return; - ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult); + ApplyResult(static (r, parentHitObject) => + { + r.Type = parentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult; + }, ParentHitObject); } public override bool OnPressed(KeyBindingPressEvent e) => false; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index c900165d34..de9a3a31c5 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -49,14 +49,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (!userTriggered) { if (timeOffset > HitObject.HitWindow) - ApplyResult(r => r.Type = r.Judgement.MinResult); + ApplyResult(static r => r.Type = r.Judgement.MinResult); return; } if (Math.Abs(timeOffset) > HitObject.HitWindow) return; - ApplyResult(r => r.Type = r.Judgement.MaxResult); + ApplyResult(static r => r.Type = r.Judgement.MaxResult); } public override void OnKilled() @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables base.OnKilled(); if (Time.Current > HitObject.GetEndTime() && !Judged) - ApplyResult(r => r.Type = r.Judgement.MinResult); + ApplyResult(static r => r.Type = r.Judgement.MinResult); } protected override void UpdateHitStateTransforms(ArmedState state) @@ -105,7 +105,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (!ParentHitObject.Judged) return; - ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult); + ApplyResult(static (r, parentHitObject) => + { + r.Type = parentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult; + }, ParentHitObject); } public override bool OnPressed(KeyBindingPressEvent e) => false; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs index a039ce3407..1332b9e950 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void LoadComplete() { base.LoadComplete(); - ApplyResult(r => r.Type = r.Judgement.MaxResult); + ApplyResult(static r => r.Type = r.Judgement.MaxResult); } protected override void LoadSamples() diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 1ef426854e..c3bd76bf81 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (!userTriggered) { if (!HitObject.HitWindows.CanBeHit(timeOffset)) - ApplyResult(r => r.Type = r.Judgement.MinResult); + ApplyResult(static r => r.Type = r.Judgement.MinResult); return; } @@ -108,9 +108,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return; if (!validActionPressed) - ApplyResult(r => r.Type = r.Judgement.MinResult); + ApplyResult(static r => r.Type = r.Judgement.MinResult); else - ApplyResult(r => r.Type = result); + ApplyResult(static (r, result) => r.Type = result, result); } public override bool OnPressed(KeyBindingPressEvent e) @@ -209,19 +209,19 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (!ParentHitObject.Result.IsHit) { - ApplyResult(r => r.Type = r.Judgement.MinResult); + ApplyResult(static r => r.Type = r.Judgement.MinResult); return; } if (!userTriggered) { if (timeOffset - ParentHitObject.Result.TimeOffset > SECOND_HIT_WINDOW) - ApplyResult(r => r.Type = r.Judgement.MinResult); + ApplyResult(static r => r.Type = r.Judgement.MinResult); return; } if (Math.Abs(timeOffset - ParentHitObject.Result.TimeOffset) <= SECOND_HIT_WINDOW) - ApplyResult(r => r.Type = r.Judgement.MaxResult); + ApplyResult(static r => r.Type = r.Judgement.MaxResult); } public override bool OnPressed(KeyBindingPressEvent e) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs index 724d59edcd..4080c14066 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables // it can happen that the hit window of the nested strong hit extends past the lifetime of the parent object. // this is a safety to prevent such cases from causing the nested hit to never be judged and as such prevent gameplay from completing. if (!Judged && Time.Current > ParentHitObject?.HitObject.GetEndTime()) - ApplyResult(r => r.Type = r.Judgement.MinResult); + ApplyResult(static r => r.Type = r.Judgement.MinResult); } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index e4a083f218..d48b78283b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -206,7 +206,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint); if (numHits == HitObject.RequiredHits) - ApplyResult(r => r.Type = r.Judgement.MaxResult); + ApplyResult(static r => r.Type = r.Judgement.MaxResult); } else { @@ -227,7 +227,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables tick.TriggerResult(false); } - ApplyResult(r => r.Type = numHits == HitObject.RequiredHits ? r.Judgement.MaxResult : r.Judgement.MinResult); + ApplyResult(static (r, state) => + { + var (numHits, hitObject) = state; + r.Type = numHits == hitObject.RequiredHits ? r.Judgement.MaxResult : r.Judgement.MinResult; + }, (numHits, HitObject)); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs index 3a5c006962..ad1d09bc7b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs @@ -30,7 +30,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public void TriggerResult(bool hit) { HitObject.StartTime = Time.Current; - ApplyResult(r => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult); + ApplyResult(static (r, hit) => + { + r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult; + }, hit); } protected override void CheckForResult(bool userTriggered, double timeOffset) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index bce28361cb..9acd7b3c0f 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -687,12 +687,14 @@ namespace osu.Game.Rulesets.Objects.Drawables /// the of the . /// /// The callback that applies changes to the . - protected void ApplyResult(Action application) + /// The state passed to the callback. + /// The type of the state information that is passed to the callback method. + protected void ApplyResult(Action application, TState state) { if (Result.HasResult) throw new InvalidOperationException("Cannot apply result on a hitobject that already has a result."); - application?.Invoke(Result); + application?.Invoke(Result, state); if (!Result.HasResult) throw new InvalidOperationException($"{GetType().ReadableName()} applied a {nameof(JudgementResult)} but did not update {nameof(JudgementResult.Type)}."); @@ -714,6 +716,13 @@ namespace osu.Game.Rulesets.Objects.Drawables OnNewResult?.Invoke(this, Result); } + /// + /// Applies the of this , notifying responders such as + /// the of the . + /// + /// The callback that applies changes to the . + protected void ApplyResult(Action application) => ApplyResult((r, _) => application?.Invoke(r), null); + /// /// Processes this , checking if a scoring result has occurred. /// From 93bd3ce5ae73eb6f00649e09ace1acd2224ecc95 Mon Sep 17 00:00:00 2001 From: Chandler Stowell Date: Thu, 25 Jan 2024 11:25:41 -0500 Subject: [PATCH 2/8] update `DrawableHitCircle.ApplyResult` to pass `this` to its callback --- .../DrawableEmptyFreeformHitObject.cs | 2 +- .../Drawables/DrawablePippidonHitObject.cs | 6 ++--- .../DrawableEmptyScrollingHitObject.cs | 2 +- .../Drawables/DrawablePippidonHitObject.cs | 5 ++-- .../Drawables/DrawableCatchHitObject.cs | 7 +++--- .../Objects/Drawables/DrawableHoldNote.cs | 2 +- .../Objects/Drawables/DrawableHoldNoteBody.cs | 9 ++++++- .../Drawables/DrawableManiaHitObject.cs | 2 +- .../Objects/Drawables/DrawableNote.cs | 16 +++++++++---- .../TestSceneHitCircle.cs | 2 +- .../TestSceneHitCircleLateFade.cs | 2 +- .../Objects/Drawables/DrawableHitCircle.cs | 19 ++++++++------- .../Objects/Drawables/DrawableOsuHitObject.cs | 4 ++-- .../Objects/Drawables/DrawableSlider.cs | 14 +++++------ .../Objects/Drawables/DrawableSpinner.cs | 5 ++-- .../Objects/Drawables/DrawableSpinnerTick.cs | 12 +++++++++- .../Objects/Drawables/DrawableDrumRoll.cs | 8 +++---- .../Objects/Drawables/DrawableDrumRollTick.cs | 13 +++++----- .../Objects/Drawables/DrawableFlyingHit.cs | 2 +- .../Objects/Drawables/DrawableHit.cs | 24 ++++++++++++------- .../Drawables/DrawableStrongNestedHit.cs | 2 +- .../Objects/Drawables/DrawableSwell.cs | 16 +++++++------ .../Objects/Drawables/DrawableSwellTick.cs | 10 +++++--- .../Gameplay/TestSceneDrawableHitObject.cs | 2 +- .../Gameplay/TestScenePoolingRuleset.cs | 8 +++---- .../Objects/Drawables/DrawableHitObject.cs | 13 ++-------- 26 files changed, 120 insertions(+), 87 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Objects/Drawables/DrawableEmptyFreeformHitObject.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Objects/Drawables/DrawableEmptyFreeformHitObject.cs index e8f511bc4b..3ad8f06fb4 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Objects/Drawables/DrawableEmptyFreeformHitObject.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Objects/Drawables/DrawableEmptyFreeformHitObject.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.EmptyFreeform.Objects.Drawables { if (timeOffset >= 0) // todo: implement judgement logic - ApplyResult(static r => r.Type = HitResult.Perfect); + ApplyResult(static (r, hitObject) => r.Type = HitResult.Perfect); } protected override void UpdateHitStateTransforms(ArmedState state) diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs index a8bb57ba18..925f2d04bf 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs @@ -50,10 +50,10 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables { if (timeOffset >= 0) { - ApplyResult(static (r, isHovered) => + ApplyResult(static (r, hitObject) => { - r.Type = isHovered ? HitResult.Perfect : HitResult.Miss; - }, IsHovered); + r.Type = hitObject.IsHovered ? HitResult.Perfect : HitResult.Miss; + }); } } diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Objects/Drawables/DrawableEmptyScrollingHitObject.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Objects/Drawables/DrawableEmptyScrollingHitObject.cs index 070a802aea..408bbea717 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Objects/Drawables/DrawableEmptyScrollingHitObject.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Objects/Drawables/DrawableEmptyScrollingHitObject.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.EmptyScrolling.Objects.Drawables { if (timeOffset >= 0) // todo: implement judgement logic - ApplyResult(static r => r.Type = HitResult.Perfect); + ApplyResult(static (r, hitObject) => r.Type = HitResult.Perfect); } protected override void UpdateHitStateTransforms(ArmedState state) diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs index 9983ec20b0..2c9eac7f65 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs @@ -50,10 +50,11 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables { if (timeOffset >= 0) { - ApplyResult(static (r, pippidonHitObject) => + ApplyResult(static (r, hitObject) => { + var pippidonHitObject = (DrawablePippidonHitObject)hitObject; r.Type = pippidonHitObject.currentLane.Value == pippidonHitObject.HitObject.Lane ? HitResult.Perfect : HitResult.Miss; - }, this); + }); } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs index 5a921f36f5..721c6aaa59 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs @@ -64,10 +64,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables if (timeOffset >= 0 && Result != null) { - ApplyResult(static (r, state) => + ApplyResult(static (r, hitObject) => { - r.Type = state.CheckPosition.Invoke(state.HitObject) ? r.Judgement.MaxResult : r.Judgement.MinResult; - }, this); + var catchHitObject = (DrawableCatchHitObject)hitObject; + r.Type = catchHitObject.CheckPosition!.Invoke(catchHitObject.HitObject) ? r.Judgement.MaxResult : r.Judgement.MinResult; + }); } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index e5056d5167..6c70ab3526 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -265,7 +265,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (Tail.AllJudged) { if (Tail.IsHit) - ApplyResult(static r => r.Type = r.Judgement.MaxResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); else MissForcefully(); } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteBody.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteBody.cs index 317da0580c..731b1b6298 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteBody.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteBody.cs @@ -11,6 +11,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables public override bool DisplayResult => false; + private bool hit; + public DrawableHoldNoteBody() : this(null) { @@ -25,7 +27,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { if (AllJudged) return; - ApplyResult(static (r, hit) => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult, hit); + this.hit = hit; + ApplyResult(static (r, hitObject) => + { + var holdNoteBody = (DrawableHoldNoteBody)hitObject; + r.Type = holdNoteBody.hit ? r.Judgement.MaxResult : r.Judgement.MinResult; + }); } } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index dea0817869..2d10fa27cd 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables /// /// Causes this to get missed, disregarding all conditions in implementations of . /// - public virtual void MissForcefully() => ApplyResult(static r => r.Type = r.Judgement.MinResult); + public virtual void MissForcefully() => ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); } public abstract partial class DrawableManiaHitObject : DrawableManiaHitObject diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 985007f905..a70253798a 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -38,6 +38,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables private Drawable headPiece; + private HitResult hitResult; + public DrawableNote() : this(null) { @@ -89,18 +91,22 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (!userTriggered) { if (!HitObject.HitWindows.CanBeHit(timeOffset)) - ApplyResult(static r => r.Type = r.Judgement.MinResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); return; } - var result = HitObject.HitWindows.ResultFor(timeOffset); - if (result == HitResult.None) + hitResult = HitObject.HitWindows.ResultFor(timeOffset); + if (hitResult == HitResult.None) return; - result = GetCappedResult(result); + hitResult = GetCappedResult(hitResult); - ApplyResult(static (r, result) => r.Type = result, result); + ApplyResult(static (r, hitObject) => + { + var note = (DrawableNote)hitObject; + r.Type = note.hitResult; + }); } /// diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs index 30b0451a3b..8d4145f2c1 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs @@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Tests if (auto && !userTriggered && timeOffset > hitOffset && CheckHittable?.Invoke(this, Time.Current, HitResult.Great) == ClickAction.Hit) { // force success - ApplyResult(r => r.Type = HitResult.Great); + ApplyResult(static (r, _) => r.Type = HitResult.Great); } else base.CheckForResult(userTriggered, timeOffset); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs index 7824f26251..2d1e9c1270 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs @@ -208,7 +208,7 @@ namespace osu.Game.Rulesets.Osu.Tests if (shouldHit && !userTriggered && timeOffset >= 0) { // force success - ApplyResult(r => r.Type = HitResult.Great); + ApplyResult(static (r, _) => r.Type = HitResult.Great); } else base.CheckForResult(userTriggered, timeOffset); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 8284229d82..ce5422b180 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -44,6 +44,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Container scaleContainer; private InputManager inputManager; + private HitResult hitResult; public DrawableHitCircle() : this(null) @@ -155,34 +156,34 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (!userTriggered) { if (!HitObject.HitWindows.CanBeHit(timeOffset)) - ApplyResult(static r => r.Type = r.Judgement.MinResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); return; } - var result = ResultFor(timeOffset); - var clickAction = CheckHittable?.Invoke(this, Time.Current, result); + hitResult = ResultFor(timeOffset); + var clickAction = CheckHittable?.Invoke(this, Time.Current, hitResult); if (clickAction == ClickAction.Shake) Shake(); - if (result == HitResult.None || clickAction != ClickAction.Hit) + if (hitResult == HitResult.None || clickAction != ClickAction.Hit) return; - ApplyResult(static (r, state) => + ApplyResult(static (r, hitObject) => { - var (hitCircle, hitResult) = state; + var hitCircle = (DrawableHitCircle)hitObject; 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 (hitResult.IsHit()) + if (hitCircle.hitResult.IsHit()) { var localMousePosition = hitCircle.ToLocalSpace(hitCircle.inputManager.CurrentState.Mouse.Position); circleResult.CursorPositionAtHit = hitCircle.HitObject.StackedPosition + (localMousePosition - hitCircle.DrawSize / 2); } - circleResult.Type = hitResult; - }, (this, result)); + circleResult.Type = hitCircle.hitResult; + }); } /// diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index cc06d009c9..6de60a9d51 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -100,12 +100,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// /// Causes this to get hit, disregarding all conditions in implementations of . /// - public void HitForcefully() => ApplyResult(static r => r.Type = r.Judgement.MaxResult); + public void HitForcefully() => ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); /// /// Causes this to get missed, disregarding all conditions in implementations of . /// - public void MissForcefully() => ApplyResult(static r => r.Type = r.Judgement.MinResult); + public void MissForcefully() => ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); private RectangleF parentScreenSpaceRectangle => ((DrawableOsuHitObject)ParentHitObject)?.parentScreenSpaceRectangle ?? Parent!.ScreenSpaceDrawQuad.AABBFloat; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 3c298cc6af..c0ff258352 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -292,10 +292,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (HitObject.ClassicSliderBehaviour) { // Classic behaviour means a slider is judged proportionally to the number of nested hitobjects hit. This is the classic osu!stable scoring. - ApplyResult(static (r, nestedHitObjects) => + ApplyResult(static (r, hitObject) => { - int totalTicks = nestedHitObjects.Count; - int hitTicks = nestedHitObjects.Count(h => h.IsHit); + int totalTicks = hitObject.NestedHitObjects.Count; + int hitTicks = hitObject.NestedHitObjects.Count(h => h.IsHit); if (hitTicks == totalTicks) r.Type = HitResult.Great; @@ -306,16 +306,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables double hitFraction = (double)hitTicks / totalTicks; r.Type = hitFraction >= 0.5 ? HitResult.Ok : HitResult.Meh; } - }, NestedHitObjects); + }); } else { // If only the nested hitobjects are judged, then the slider's own judgement is ignored for scoring purposes. // But the slider needs to still be judged with a reasonable hit/miss result for visual purposes (hit/miss transforms, etc). - ApplyResult(static (r, nestedHitObjects) => + ApplyResult(static (r, hitObject) => { - r.Type = nestedHitObjects.Any(h => h.Result.IsHit) ? r.Judgement.MaxResult : r.Judgement.MinResult; - }, NestedHitObjects); + r.Type = hitObject.NestedHitObjects.Any(h => h.Result.IsHit) ? r.Judgement.MaxResult : r.Judgement.MinResult; + }); } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index d21d02c8ce..3679bc9775 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -258,8 +258,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables foreach (var tick in ticks.Where(t => !t.Result.HasResult)) tick.TriggerResult(false); - ApplyResult(static (r, spinner) => + ApplyResult(static (r, hitObject) => { + var spinner = (DrawableSpinner)hitObject; if (spinner.Progress >= 1) r.Type = HitResult.Great; else if (spinner.Progress > .9) @@ -268,7 +269,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables r.Type = HitResult.Meh; else if (spinner.Time.Current >= spinner.HitObject.EndTime) r.Type = r.Judgement.MinResult; - }, this); + }); } protected override void Update() diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs index 1c3ff29118..628f07a281 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs @@ -11,6 +11,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public override bool DisplayResult => false; + private bool hit; + public DrawableSpinnerTick() : this(null) { @@ -35,6 +37,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// Apply a judgement result. /// /// Whether this tick was reached. - internal void TriggerResult(bool hit) => ApplyResult(static (r, hit) => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult, hit); + internal void TriggerResult(bool hit) + { + this.hit = hit; + ApplyResult(static (r, hitObject) => + { + var spinnerTick = (DrawableSpinnerTick)hitObject; + r.Type = spinnerTick.hit ? r.Judgement.MaxResult : r.Judgement.MinResult; + }); + } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index d3fe363857..2e40875af1 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (timeOffset < 0) return; - ApplyResult(static r => r.Type = r.Judgement.MaxResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); } protected override void UpdateHitStateTransforms(ArmedState state) @@ -192,10 +192,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (!ParentHitObject.Judged) return; - ApplyResult(static (r, parentHitObject) => + ApplyResult(static (r, hitObject) => { - r.Type = parentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult; - }, ParentHitObject); + r.Type = hitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult; + }); } public override bool OnPressed(KeyBindingPressEvent e) => false; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index de9a3a31c5..aa678d7043 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -49,14 +49,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (!userTriggered) { if (timeOffset > HitObject.HitWindow) - ApplyResult(static r => r.Type = r.Judgement.MinResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); return; } if (Math.Abs(timeOffset) > HitObject.HitWindow) return; - ApplyResult(static r => r.Type = r.Judgement.MaxResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); } public override void OnKilled() @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables base.OnKilled(); if (Time.Current > HitObject.GetEndTime() && !Judged) - ApplyResult(static r => r.Type = r.Judgement.MinResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); } protected override void UpdateHitStateTransforms(ArmedState state) @@ -105,10 +105,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (!ParentHitObject.Judged) return; - ApplyResult(static (r, parentHitObject) => + ApplyResult(static (r, hitObject) => { - r.Type = parentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult; - }, ParentHitObject); + var nestedHit = (StrongNestedHit)hitObject; + r.Type = nestedHit.ParentHitObject!.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult; + }); } public override bool OnPressed(KeyBindingPressEvent e) => false; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs index 1332b9e950..4349dff9f9 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void LoadComplete() { base.LoadComplete(); - ApplyResult(static r => r.Type = r.Judgement.MaxResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); } protected override void LoadSamples() diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index c3bd76bf81..cf8e4050ee 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -37,6 +37,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private double? lastPressHandleTime; + private HitResult hitResult; + private readonly Bindable type = new Bindable(); public DrawableHit() @@ -99,18 +101,24 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (!userTriggered) { if (!HitObject.HitWindows.CanBeHit(timeOffset)) - ApplyResult(static r => r.Type = r.Judgement.MinResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); return; } - var result = HitObject.HitWindows.ResultFor(timeOffset); - if (result == HitResult.None) + hitResult = HitObject.HitWindows.ResultFor(timeOffset); + if (hitResult == HitResult.None) return; if (!validActionPressed) - ApplyResult(static r => r.Type = r.Judgement.MinResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); else - ApplyResult(static (r, result) => r.Type = result, result); + { + ApplyResult(static (r, hitObject) => + { + var drawableHit = (DrawableHit)hitObject; + r.Type = drawableHit.hitResult; + }); + } } public override bool OnPressed(KeyBindingPressEvent e) @@ -209,19 +217,19 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (!ParentHitObject.Result.IsHit) { - ApplyResult(static r => r.Type = r.Judgement.MinResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); return; } if (!userTriggered) { if (timeOffset - ParentHitObject.Result.TimeOffset > SECOND_HIT_WINDOW) - ApplyResult(static r => r.Type = r.Judgement.MinResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); return; } if (Math.Abs(timeOffset - ParentHitObject.Result.TimeOffset) <= SECOND_HIT_WINDOW) - ApplyResult(static r => r.Type = r.Judgement.MaxResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); } public override bool OnPressed(KeyBindingPressEvent e) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs index 4080c14066..8f99538448 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables // it can happen that the hit window of the nested strong hit extends past the lifetime of the parent object. // this is a safety to prevent such cases from causing the nested hit to never be judged and as such prevent gameplay from completing. if (!Judged && Time.Current > ParentHitObject?.HitObject.GetEndTime()) - ApplyResult(static r => r.Type = r.Judgement.MinResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index d48b78283b..0781ea5e2a 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -41,6 +41,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private double? lastPressHandleTime; + private int numHits; + public override bool DisplayResult => false; public DrawableSwell() @@ -192,7 +194,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables nextTick?.TriggerResult(true); - int numHits = ticks.Count(r => r.IsHit); + numHits = ticks.Count(r => r.IsHit); float completion = (float)numHits / HitObject.RequiredHits; @@ -206,14 +208,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint); if (numHits == HitObject.RequiredHits) - ApplyResult(static r => r.Type = r.Judgement.MaxResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); } else { if (timeOffset < 0) return; - int numHits = 0; + numHits = 0; foreach (var tick in ticks) { @@ -227,11 +229,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables tick.TriggerResult(false); } - ApplyResult(static (r, state) => + ApplyResult(static (r, hitObject) => { - var (numHits, hitObject) = state; - r.Type = numHits == hitObject.RequiredHits ? r.Judgement.MaxResult : r.Judgement.MinResult; - }, (numHits, HitObject)); + var swell = (DrawableSwell)hitObject; + r.Type = swell.numHits == swell.HitObject.RequiredHits ? r.Judgement.MaxResult : r.Judgement.MinResult; + }); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs index ad1d09bc7b..557438e5e5 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs @@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { public override bool DisplayResult => false; + private bool hit; + public DrawableSwellTick() : this(null) { @@ -29,11 +31,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public void TriggerResult(bool hit) { + this.hit = hit; HitObject.StartTime = Time.Current; - ApplyResult(static (r, hit) => + ApplyResult(static (r, hitObject) => { - r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult; - }, hit); + var swellTick = (DrawableSwellTick)hitObject; + r.Type = swellTick.hit ? r.Judgement.MaxResult : r.Judgement.MinResult; + }); } protected override void CheckForResult(bool userTriggered, double timeOffset) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs index 10dbede2e0..bf1e52aab5 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs @@ -216,7 +216,7 @@ namespace osu.Game.Tests.Gameplay LifetimeStart = LIFETIME_ON_APPLY; } - public void MissForcefully() => ApplyResult(r => r.Type = HitResult.Miss); + public void MissForcefully() => ApplyResult(static (r, _) => r.Type = HitResult.Miss); protected override void UpdateHitStateTransforms(ArmedState state) { diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs index fea7456472..00bd58e303 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs @@ -431,7 +431,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected override void CheckForResult(bool userTriggered, double timeOffset) { if (timeOffset > HitObject.Duration) - ApplyResult(r => r.Type = r.Judgement.MaxResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); } protected override void UpdateHitStateTransforms(ArmedState state) @@ -468,7 +468,7 @@ namespace osu.Game.Tests.Visual.Gameplay public override void OnKilled() { base.OnKilled(); - ApplyResult(r => r.Type = r.Judgement.MinResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); } } @@ -547,7 +547,7 @@ namespace osu.Game.Tests.Visual.Gameplay { base.CheckForResult(userTriggered, timeOffset); if (timeOffset >= 0) - ApplyResult(r => r.Type = r.Judgement.MaxResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); } } @@ -596,7 +596,7 @@ namespace osu.Game.Tests.Visual.Gameplay { base.CheckForResult(userTriggered, timeOffset); if (timeOffset >= 0) - ApplyResult(r => r.Type = r.Judgement.MaxResult); + ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); } } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 9acd7b3c0f..bffe174be1 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -687,14 +687,12 @@ namespace osu.Game.Rulesets.Objects.Drawables /// the of the . /// /// The callback that applies changes to the . - /// The state passed to the callback. - /// The type of the state information that is passed to the callback method. - protected void ApplyResult(Action application, TState state) + protected void ApplyResult(Action application) { if (Result.HasResult) throw new InvalidOperationException("Cannot apply result on a hitobject that already has a result."); - application?.Invoke(Result, state); + application?.Invoke(Result, this); if (!Result.HasResult) throw new InvalidOperationException($"{GetType().ReadableName()} applied a {nameof(JudgementResult)} but did not update {nameof(JudgementResult.Type)}."); @@ -716,13 +714,6 @@ namespace osu.Game.Rulesets.Objects.Drawables OnNewResult?.Invoke(this, Result); } - /// - /// Applies the of this , notifying responders such as - /// the of the . - /// - /// The callback that applies changes to the . - protected void ApplyResult(Action application) => ApplyResult((r, _) => application?.Invoke(r), null); - /// /// Processes this , checking if a scoring result has occurred. /// From 682dab5d8327e24b38914f0599d8822eddf05e93 Mon Sep 17 00:00:00 2001 From: Chandler Stowell Date: Thu, 25 Jan 2024 11:30:52 -0500 Subject: [PATCH 3/8] check if parent was hit in taiko's `DrawableDrumRoll.CheckForResult` --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 2e40875af1..f68198b967 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -194,7 +194,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ApplyResult(static (r, hitObject) => { - r.Type = hitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult; + var drumRoll = (DrawableDrumRoll)hitObject; + r.Type = drumRoll.ParentHitObject!.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult; }); } From 347e88f59772f0dc0045f46dc1c8e060ed8b51b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Jan 2024 16:21:48 +0900 Subject: [PATCH 4/8] Add note about using `static` callback --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index bffe174be1..e30ce13f08 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -686,7 +686,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// Applies the of this , notifying responders such as /// the of the . /// - /// The callback that applies changes to the . + /// The callback that applies changes to the . Using a `static` delegate is recommended to avoid allocation overhead. protected void ApplyResult(Action application) { if (Result.HasResult) From 6cfd2813ede27126f002ace78e3def0e5f7b03f5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Jan 2024 16:52:03 +0900 Subject: [PATCH 5/8] Fix incorrect cast --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index f68198b967..e15298f3ca 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -194,7 +194,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ApplyResult(static (r, hitObject) => { - var drumRoll = (DrawableDrumRoll)hitObject; + var drumRoll = (StrongNestedHit)hitObject; r.Type = drumRoll.ParentHitObject!.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult; }); } From efe6bb25b14db26e7c0b79494aa3eaf038dd99b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 5 Feb 2024 13:21:01 +0100 Subject: [PATCH 6/8] Refactor result application around again to remove requirement for fields Co-authored-by: Dean Herbert --- .../Objects/Drawables/DrawableHoldNote.cs | 2 +- .../Objects/Drawables/DrawableHoldNoteBody.cs | 12 +++----- .../Drawables/DrawableManiaHitObject.cs | 2 +- .../Objects/Drawables/DrawableNote.cs | 3 +- .../TestSceneHitCircle.cs | 2 +- .../TestSceneHitCircleLateFade.cs | 2 +- .../Objects/Drawables/DrawableHitCircle.cs | 29 ++++++++++--------- .../Objects/Drawables/DrawableOsuHitObject.cs | 4 +-- .../Objects/Drawables/DrawableDrumRoll.cs | 2 +- .../Objects/Drawables/DrawableDrumRollTick.cs | 6 ++-- .../Objects/Drawables/DrawableFlyingHit.cs | 2 +- .../Objects/Drawables/DrawableHit.cs | 10 +++---- .../Drawables/DrawableStrongNestedHit.cs | 2 +- .../Objects/Drawables/DrawableSwell.cs | 2 +- .../Gameplay/TestSceneDrawableHitObject.cs | 2 +- .../Gameplay/TestScenePoolingRuleset.cs | 8 ++--- .../Rulesets/Judgements/JudgementResult.cs | 2 +- .../Objects/Drawables/DrawableHitObject.cs | 17 +++++++++-- 18 files changed, 59 insertions(+), 50 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 6c70ab3526..2b55e81788 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -265,7 +265,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (Tail.AllJudged) { if (Tail.IsHit) - ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); + ApplyMaxResult(); else MissForcefully(); } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteBody.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteBody.cs index 731b1b6298..6259033235 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteBody.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteBody.cs @@ -11,8 +11,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables public override bool DisplayResult => false; - private bool hit; - public DrawableHoldNoteBody() : this(null) { @@ -27,12 +25,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { if (AllJudged) return; - this.hit = hit; - ApplyResult(static (r, hitObject) => - { - var holdNoteBody = (DrawableHoldNoteBody)hitObject; - r.Type = holdNoteBody.hit ? r.Judgement.MaxResult : r.Judgement.MinResult; - }); + if (hit) + ApplyMaxResult(); + else + ApplyMinResult(); } } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 2d10fa27cd..e98622b8bf 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables /// /// Causes this to get missed, disregarding all conditions in implementations of . /// - public virtual void MissForcefully() => ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); + public virtual void MissForcefully() => ApplyMinResult(); } public abstract partial class DrawableManiaHitObject : DrawableManiaHitObject diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index a70253798a..2246552abe 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -91,12 +91,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (!userTriggered) { if (!HitObject.HitWindows.CanBeHit(timeOffset)) - ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); + ApplyMinResult(); return; } hitResult = HitObject.HitWindows.ResultFor(timeOffset); + if (hitResult == HitResult.None) return; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs index 8d4145f2c1..abe950f9bb 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs @@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Tests if (auto && !userTriggered && timeOffset > hitOffset && CheckHittable?.Invoke(this, Time.Current, HitResult.Great) == ClickAction.Hit) { // force success - ApplyResult(static (r, _) => r.Type = HitResult.Great); + ApplyResult(HitResult.Great); } else base.CheckForResult(userTriggered, timeOffset); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs index 2d1e9c1270..838b426cb4 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs @@ -208,7 +208,7 @@ namespace osu.Game.Rulesets.Osu.Tests if (shouldHit && !userTriggered && timeOffset >= 0) { // force success - ApplyResult(static (r, _) => r.Type = HitResult.Great); + ApplyResult(HitResult.Great); } else base.CheckForResult(userTriggered, timeOffset); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index ce5422b180..a014ba2e77 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -44,7 +44,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Container scaleContainer; private InputManager inputManager; - private HitResult hitResult; public DrawableHitCircle() : this(null) @@ -156,12 +155,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (!userTriggered) { if (!HitObject.HitWindows.CanBeHit(timeOffset)) - ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); + ApplyMinResult(); return; } - hitResult = ResultFor(timeOffset); + HitResult hitResult = ResultFor(timeOffset); var clickAction = CheckHittable?.Invoke(this, Time.Current, hitResult); if (clickAction == ClickAction.Shake) @@ -170,20 +169,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (hitResult == HitResult.None || clickAction != ClickAction.Hit) return; - ApplyResult(static (r, hitObject) => + Vector2? hitPosition = null; + + // 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 (hitResult.IsHit()) + { + var localMousePosition = ToLocalSpace(inputManager.CurrentState.Mouse.Position); + hitPosition = HitObject.StackedPosition + (localMousePosition - DrawSize / 2); + } + + ApplyResult<(HitResult result, Vector2? position)>((r, state) => { - var hitCircle = (DrawableHitCircle)hitObject; 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 (hitCircle.hitResult.IsHit()) - { - var localMousePosition = hitCircle.ToLocalSpace(hitCircle.inputManager.CurrentState.Mouse.Position); - circleResult.CursorPositionAtHit = hitCircle.HitObject.StackedPosition + (localMousePosition - hitCircle.DrawSize / 2); - } - - circleResult.Type = hitCircle.hitResult; - }); + circleResult.Type = state.result; + circleResult.CursorPositionAtHit = state.position; + }, (hitResult, hitPosition)); } /// diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 6de60a9d51..5271c03e08 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -100,12 +100,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// /// Causes this to get hit, disregarding all conditions in implementations of . /// - public void HitForcefully() => ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); + public void HitForcefully() => ApplyMaxResult(); /// /// Causes this to get missed, disregarding all conditions in implementations of . /// - public void MissForcefully() => ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); + public void MissForcefully() => ApplyMinResult(); private RectangleF parentScreenSpaceRectangle => ((DrawableOsuHitObject)ParentHitObject)?.parentScreenSpaceRectangle ?? Parent!.ScreenSpaceDrawQuad.AABBFloat; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index e15298f3ca..1af4719b02 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (timeOffset < 0) return; - ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); + ApplyMaxResult(); } protected override void UpdateHitStateTransforms(ArmedState state) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index aa678d7043..0333fd71a9 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -49,14 +49,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (!userTriggered) { if (timeOffset > HitObject.HitWindow) - ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); + ApplyMinResult(); return; } if (Math.Abs(timeOffset) > HitObject.HitWindow) return; - ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); + ApplyMaxResult(); } public override void OnKilled() @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables base.OnKilled(); if (Time.Current > HitObject.GetEndTime() && !Judged) - ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); + ApplyMinResult(); } protected override void UpdateHitStateTransforms(ArmedState state) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs index 4349dff9f9..aad9214c5e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableFlyingHit.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void LoadComplete() { base.LoadComplete(); - ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); + ApplyMaxResult(); } protected override void LoadSamples() diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index cf8e4050ee..ca49ddb7e1 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (!userTriggered) { if (!HitObject.HitWindows.CanBeHit(timeOffset)) - ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); + ApplyMinResult(); return; } @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return; if (!validActionPressed) - ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); + ApplyMinResult(); else { ApplyResult(static (r, hitObject) => @@ -217,19 +217,19 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (!ParentHitObject.Result.IsHit) { - ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); + ApplyMinResult(); return; } if (!userTriggered) { if (timeOffset - ParentHitObject.Result.TimeOffset > SECOND_HIT_WINDOW) - ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); + ApplyMinResult(); return; } if (Math.Abs(timeOffset - ParentHitObject.Result.TimeOffset) <= SECOND_HIT_WINDOW) - ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); + ApplyMaxResult(); } public override bool OnPressed(KeyBindingPressEvent e) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs index 8f99538448..11759927a9 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables // it can happen that the hit window of the nested strong hit extends past the lifetime of the parent object. // this is a safety to prevent such cases from causing the nested hit to never be judged and as such prevent gameplay from completing. if (!Judged && Time.Current > ParentHitObject?.HitObject.GetEndTime()) - ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); + ApplyMinResult(); } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 0781ea5e2a..6eb62cce22 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -208,7 +208,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint); if (numHits == HitObject.RequiredHits) - ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); + ApplyMaxResult(); } else { diff --git a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs index bf1e52aab5..73177e36e1 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs @@ -216,7 +216,7 @@ namespace osu.Game.Tests.Gameplay LifetimeStart = LIFETIME_ON_APPLY; } - public void MissForcefully() => ApplyResult(static (r, _) => r.Type = HitResult.Miss); + public void MissForcefully() => ApplyResult(HitResult.Miss); protected override void UpdateHitStateTransforms(ArmedState state) { diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs index 00bd58e303..b567e8de8d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs @@ -431,7 +431,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected override void CheckForResult(bool userTriggered, double timeOffset) { if (timeOffset > HitObject.Duration) - ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); + ApplyMaxResult(); } protected override void UpdateHitStateTransforms(ArmedState state) @@ -468,7 +468,7 @@ namespace osu.Game.Tests.Visual.Gameplay public override void OnKilled() { base.OnKilled(); - ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult); + ApplyMinResult(); } } @@ -547,7 +547,7 @@ namespace osu.Game.Tests.Visual.Gameplay { base.CheckForResult(userTriggered, timeOffset); if (timeOffset >= 0) - ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); + ApplyMaxResult(); } } @@ -596,7 +596,7 @@ namespace osu.Game.Tests.Visual.Gameplay { base.CheckForResult(userTriggered, timeOffset); if (timeOffset >= 0) - ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult); + ApplyMaxResult(); } } diff --git a/osu.Game/Rulesets/Judgements/JudgementResult.cs b/osu.Game/Rulesets/Judgements/JudgementResult.cs index b781a13929..4b98df50d7 100644 --- a/osu.Game/Rulesets/Judgements/JudgementResult.cs +++ b/osu.Game/Rulesets/Judgements/JudgementResult.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Judgements /// /// The time at which this occurred. - /// Populated when this is applied via . + /// Populated when this is applied via . /// /// /// This is used instead of to check whether this should be reverted. diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index e30ce13f08..07fab72814 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -682,17 +682,28 @@ namespace osu.Game.Rulesets.Objects.Drawables UpdateResult(false); } + protected void ApplyMaxResult() => ApplyResult((r, _) => r.Type = r.Judgement.MaxResult); + protected void ApplyMinResult() => ApplyResult((r, _) => r.Type = r.Judgement.MinResult); + + protected void ApplyResult(HitResult type) => ApplyResult(static (result, state) => result.Type = state, type); + + [Obsolete("Use overload with state, preferrably with static delegates to avoid allocation overhead.")] // Can be removed 2024-07-26 + protected void ApplyResult(Action application) => ApplyResult((r, _) => application(r), this); + + protected void ApplyResult(Action application) => ApplyResult(application, this); + /// /// Applies the of this , notifying responders such as /// the of the . /// /// The callback that applies changes to the . Using a `static` delegate is recommended to avoid allocation overhead. - protected void ApplyResult(Action application) + /// The state. + protected void ApplyResult(Action application, T state) { if (Result.HasResult) throw new InvalidOperationException("Cannot apply result on a hitobject that already has a result."); - application?.Invoke(Result, this); + application?.Invoke(Result, state); if (!Result.HasResult) throw new InvalidOperationException($"{GetType().ReadableName()} applied a {nameof(JudgementResult)} but did not update {nameof(JudgementResult.Type)}."); @@ -737,7 +748,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// Checks if a scoring result has occurred for this . /// /// - /// If a scoring result has occurred, this method must invoke to update the result and notify responders. + /// If a scoring result has occurred, this method must invoke to update the result and notify responders. /// /// Whether the user triggered this check. /// The offset from the end time of the at which this check occurred. From 2976f225e0627ffa8cb4efe2ad6060e02a6fa5ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 5 Feb 2024 13:22:58 +0100 Subject: [PATCH 7/8] Improve xmldoc of `state` param --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 07fab72814..c5f1878d1f 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -697,7 +697,10 @@ namespace osu.Game.Rulesets.Objects.Drawables /// the of the . /// /// The callback that applies changes to the . Using a `static` delegate is recommended to avoid allocation overhead. - /// The state. + /// + /// Use this parameter to pass any data that requires + /// to apply a result, so that it can remain a `static` delegate and thus not allocate. + /// protected void ApplyResult(Action application, T state) { if (Result.HasResult) From fb80d76b4a814a8b79ce441833515f450ab8e12b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 5 Feb 2024 13:35:41 +0100 Subject: [PATCH 8/8] Apply further changes to remove remaining weirdness --- .../Drawables/DrawableEmptyFreeformHitObject.cs | 2 +- .../Drawables/DrawablePippidonHitObject.cs | 9 ++++----- .../Drawables/DrawableEmptyScrollingHitObject.cs | 3 +-- .../Drawables/DrawablePippidonHitObject.cs | 10 ++++------ .../Objects/Drawables/DrawableCatchHitObject.cs | 9 ++++----- .../Objects/Drawables/DrawableNote.cs | 15 ++++----------- .../Objects/Drawables/DrawableHitCircle.cs | 10 +++++----- .../Objects/Drawables/DrawableSpinnerTick.cs | 12 ++++-------- .../Objects/Drawables/DrawableHit.cs | 14 +++----------- .../Objects/Drawables/DrawableSwell.cs | 15 ++++++--------- .../Objects/Drawables/DrawableSwellTick.cs | 13 +++++-------- 11 files changed, 41 insertions(+), 71 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Objects/Drawables/DrawableEmptyFreeformHitObject.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Objects/Drawables/DrawableEmptyFreeformHitObject.cs index 3ad8f06fb4..e53fe01157 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Objects/Drawables/DrawableEmptyFreeformHitObject.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Objects/Drawables/DrawableEmptyFreeformHitObject.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.EmptyFreeform.Objects.Drawables { if (timeOffset >= 0) // todo: implement judgement logic - ApplyResult(static (r, hitObject) => r.Type = HitResult.Perfect); + ApplyResult(HitResult.Perfect); } protected override void UpdateHitStateTransforms(ArmedState state) diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs index 925f2d04bf..b1be25727f 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Audio; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Scoring; using osuTK; using osuTK.Graphics; @@ -50,10 +49,10 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables { if (timeOffset >= 0) { - ApplyResult(static (r, hitObject) => - { - r.Type = hitObject.IsHovered ? HitResult.Perfect : HitResult.Miss; - }); + if (IsHovered) + ApplyMaxResult(); + else + ApplyMinResult(); } } diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Objects/Drawables/DrawableEmptyScrollingHitObject.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Objects/Drawables/DrawableEmptyScrollingHitObject.cs index 408bbea717..adcbd36485 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Objects/Drawables/DrawableEmptyScrollingHitObject.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Objects/Drawables/DrawableEmptyScrollingHitObject.cs @@ -3,7 +3,6 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Scoring; using osuTK; using osuTK.Graphics; @@ -24,7 +23,7 @@ namespace osu.Game.Rulesets.EmptyScrolling.Objects.Drawables { if (timeOffset >= 0) // todo: implement judgement logic - ApplyResult(static (r, hitObject) => r.Type = HitResult.Perfect); + ApplyMaxResult(); } protected override void UpdateHitStateTransforms(ArmedState state) diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs index 2c9eac7f65..3ad636a601 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Textures; using osu.Game.Audio; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Pippidon.UI; -using osu.Game.Rulesets.Scoring; using osuTK; using osuTK.Graphics; @@ -50,11 +49,10 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables { if (timeOffset >= 0) { - ApplyResult(static (r, hitObject) => - { - var pippidonHitObject = (DrawablePippidonHitObject)hitObject; - r.Type = pippidonHitObject.currentLane.Value == pippidonHitObject.HitObject.Lane ? HitResult.Perfect : HitResult.Miss; - }); + if (currentLane.Value == HitObject.Lane) + ApplyMaxResult(); + else + ApplyMinResult(); } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs index 721c6aaa59..64705f9909 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs @@ -64,11 +64,10 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables if (timeOffset >= 0 && Result != null) { - ApplyResult(static (r, hitObject) => - { - var catchHitObject = (DrawableCatchHitObject)hitObject; - r.Type = catchHitObject.CheckPosition!.Invoke(catchHitObject.HitObject) ? r.Judgement.MaxResult : r.Judgement.MinResult; - }); + if (CheckPosition.Invoke(HitObject)) + ApplyMaxResult(); + else + ApplyMinResult(); } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 2246552abe..f6b92ab405 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -38,8 +38,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables private Drawable headPiece; - private HitResult hitResult; - public DrawableNote() : this(null) { @@ -96,18 +94,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables return; } - hitResult = HitObject.HitWindows.ResultFor(timeOffset); + var result = HitObject.HitWindows.ResultFor(timeOffset); - if (hitResult == HitResult.None) + if (result == HitResult.None) return; - hitResult = GetCappedResult(hitResult); - - ApplyResult(static (r, hitObject) => - { - var note = (DrawableNote)hitObject; - r.Type = note.hitResult; - }); + result = GetCappedResult(result); + ApplyResult(result); } /// diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index a014ba2e77..b1c9bef6c4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -160,19 +160,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables return; } - HitResult hitResult = ResultFor(timeOffset); - var clickAction = CheckHittable?.Invoke(this, Time.Current, hitResult); + var result = ResultFor(timeOffset); + var clickAction = CheckHittable?.Invoke(this, Time.Current, result); if (clickAction == ClickAction.Shake) Shake(); - if (hitResult == HitResult.None || clickAction != ClickAction.Hit) + if (result == HitResult.None || clickAction != ClickAction.Hit) return; Vector2? hitPosition = null; // 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 (hitResult.IsHit()) + if (result.IsHit()) { var localMousePosition = ToLocalSpace(inputManager.CurrentState.Mouse.Position); hitPosition = HitObject.StackedPosition + (localMousePosition - DrawSize / 2); @@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables circleResult.Type = state.result; circleResult.CursorPositionAtHit = state.position; - }, (hitResult, hitPosition)); + }, (result, hitPosition)); } /// diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs index 628f07a281..0a77faf924 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs @@ -11,8 +11,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public override bool DisplayResult => false; - private bool hit; - public DrawableSpinnerTick() : this(null) { @@ -39,12 +37,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// Whether this tick was reached. internal void TriggerResult(bool hit) { - this.hit = hit; - ApplyResult(static (r, hitObject) => - { - var spinnerTick = (DrawableSpinnerTick)hitObject; - r.Type = spinnerTick.hit ? r.Judgement.MaxResult : r.Judgement.MinResult; - }); + if (hit) + ApplyMaxResult(); + else + ApplyMinResult(); } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index ca49ddb7e1..4fb69056da 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -37,8 +37,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private double? lastPressHandleTime; - private HitResult hitResult; - private readonly Bindable type = new Bindable(); public DrawableHit() @@ -105,20 +103,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return; } - hitResult = HitObject.HitWindows.ResultFor(timeOffset); - if (hitResult == HitResult.None) + var result = HitObject.HitWindows.ResultFor(timeOffset); + if (result == HitResult.None) return; if (!validActionPressed) ApplyMinResult(); else - { - ApplyResult(static (r, hitObject) => - { - var drawableHit = (DrawableHit)hitObject; - r.Type = drawableHit.hitResult; - }); - } + ApplyResult(result); } public override bool OnPressed(KeyBindingPressEvent e) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 6eb62cce22..e1fc28fe16 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -41,8 +41,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private double? lastPressHandleTime; - private int numHits; - public override bool DisplayResult => false; public DrawableSwell() @@ -194,7 +192,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables nextTick?.TriggerResult(true); - numHits = ticks.Count(r => r.IsHit); + int numHits = ticks.Count(r => r.IsHit); float completion = (float)numHits / HitObject.RequiredHits; @@ -215,7 +213,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (timeOffset < 0) return; - numHits = 0; + int numHits = 0; foreach (var tick in ticks) { @@ -229,11 +227,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables tick.TriggerResult(false); } - ApplyResult(static (r, hitObject) => - { - var swell = (DrawableSwell)hitObject; - r.Type = swell.numHits == swell.HitObject.RequiredHits ? r.Judgement.MaxResult : r.Judgement.MinResult; - }); + if (numHits == HitObject.RequiredHits) + ApplyMaxResult(); + else + ApplyMinResult(); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs index 557438e5e5..04dd01e066 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs @@ -15,8 +15,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { public override bool DisplayResult => false; - private bool hit; - public DrawableSwellTick() : this(null) { @@ -31,13 +29,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public void TriggerResult(bool hit) { - this.hit = hit; HitObject.StartTime = Time.Current; - ApplyResult(static (r, hitObject) => - { - var swellTick = (DrawableSwellTick)hitObject; - r.Type = swellTick.hit ? r.Judgement.MaxResult : r.Judgement.MinResult; - }); + + if (hit) + ApplyMaxResult(); + else + ApplyMinResult(); } protected override void CheckForResult(bool userTriggered, double timeOffset)