From 2985c4cee87805f28ff259c61870ef632f71e242 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 19 Nov 2023 20:03:58 +0100 Subject: [PATCH 1/2] Lighten slide only after start time. --- .../Objects/Drawables/DrawableSlideBody.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs index 591186d31..7b8bb3025 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Judgements; @@ -69,7 +70,7 @@ private void load() AddRangeInternal(new Drawable[] { - Slidepath = new SlideVisual(), + Slidepath = new SlideVisual() { Colour = Color4.LightGray.Darken(0.25f) }, SlideStars = new Container { Anchor = Anchor.Centre, @@ -140,6 +141,7 @@ protected override void UpdateInitialTransforms() { base.UpdateInitialTransforms(); Slidepath.PerformEntryAnimation(AnimationDuration.Value); + Slidepath.Delay(AnimationDuration.Value).FadeColour(Color4.White); using (BeginAbsoluteSequence(HitObject.StartTime - 50)) { From 6a516f7e5798247c352dd272af54c11a742c1bac Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 19 Nov 2023 21:04:13 +0100 Subject: [PATCH 2/2] Slides are hittable only after SlideTap is judged --- .../Objects/Drawables/DrawableSlideBody.cs | 26 +++++++++++++++---- .../Drawables/DrawableSlideCheckpoint.cs | 10 +++---- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs index 7b8bb3025..e96b81374 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs @@ -18,9 +18,13 @@ namespace osu.Game.Rulesets.Sentakki.Objects.Drawables { public partial class DrawableSlideBody : DrawableSentakkiLanedHitObject { + public override bool RemoveWhenNotAlive => false; + + private new DrawableSlide ParentHitObject => (DrawableSlide)base.ParentHitObject; public new SlideBody HitObject => (SlideBody)base.HitObject; - public override bool RemoveWhenNotAlive => false; + // This slide body can only be interacted with iff the slidetap associated with this slide is judged + public bool IsHittable => ParentHitObject is not null && ParentHitObject.SlideTaps.Child.Judged; public Container SlideCheckpoints { get; private set; } = null!; @@ -50,6 +54,9 @@ public virtual float StarProgress } } + private static readonly Color4 inactive_color = Color4.LightGray.Darken(0.25f); + private static readonly Color4 active_color = Color4.White; + public DrawableSlideBody() : this(null) { @@ -70,7 +77,7 @@ private void load() AddRangeInternal(new Drawable[] { - Slidepath = new SlideVisual() { Colour = Color4.LightGray.Darken(0.25f) }, + Slidepath = new SlideVisual() { Colour = inactive_color }, SlideStars = new Container { Anchor = Anchor.Centre, @@ -137,11 +144,17 @@ private void onRevertResult(DrawableHitObject hitObject, JudgementResult result) Slidepath.UpdateChevronVisibility(); } + protected override void Update() + { + base.Update(); + + Slidepath.Colour = IsHittable ? active_color : inactive_color; + } + protected override void UpdateInitialTransforms() { base.UpdateInitialTransforms(); Slidepath.PerformEntryAnimation(AnimationDuration.Value); - Slidepath.Delay(AnimationDuration.Value).FadeColour(Color4.White); using (BeginAbsoluteSequence(HitObject.StartTime - 50)) { @@ -175,9 +188,10 @@ protected override void CheckForResult(bool userTriggered, double timeOffset) { Debug.Assert(HitObject.HitWindows != null); - // Player completed all nodes, we consider this user triggered + // We start with the assumption that the player completed all checkpoints userTriggered = true; + // If any of the checkpoints aren't complete, we consider the slide to be incomplete for (int i = 0; i < SlideCheckpoints.Count; ++i) { if (!SlideCheckpoints[i].Result.HasResult) @@ -186,7 +200,6 @@ protected override void CheckForResult(bool userTriggered, double timeOffset) break; } } - if (!userTriggered) { if (!HitObject.HitWindows.CanBeHit(timeOffset)) @@ -195,6 +208,7 @@ protected override void CheckForResult(bool userTriggered, double timeOffset) foreach (var checkpoint in SlideCheckpoints) checkpoint.ForcefullyMiss(); + // Apply a leniency if the player almost completed the slide if (SlideCheckpoints.Count(node => !node.Result.IsHit) <= 2 && SlideCheckpoints.Count > 2) ApplyResult(HitResult.Ok); else @@ -205,6 +219,8 @@ protected override void CheckForResult(bool userTriggered, double timeOffset) } var result = HitObject.HitWindows.ResultFor(timeOffset); + + // If the slide was completed before the early windows, just give an OK result if (result == HitResult.None) result = HitResult.Ok; diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideCheckpoint.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideCheckpoint.cs index f60ec6c94..94e72ff98 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideCheckpoint.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideCheckpoint.cs @@ -18,17 +18,17 @@ public partial class DrawableSlideCheckpoint : DrawableSentakkiHitObject public override bool DisplayResult => false; - private DrawableSlideBody parentSlide => (DrawableSlideBody)ParentHitObject; + private new DrawableSlideBody ParentHitObject => (DrawableSlideBody)base.ParentHitObject; // Used to determine the node order public int ThisIndex; // Hits are only possible if this the second node before this one is hit // If the second node before this one doesn't exist, it is allowed as this is one of the first nodes - // All hits can only be done after the parent StartTime - public bool IsHittable => Time.Current > ParentHitObject.HitObject.StartTime && isPreviousNodeHit(); + // All hits can only be done after the slide tap has been judged + public bool IsHittable => ParentHitObject.IsHittable && isPreviousNodeHit(); - private bool isPreviousNodeHit() => ThisIndex < 1 || parentSlide.SlideCheckpoints[ThisIndex - 1].IsHit; + private bool isPreviousNodeHit() => ThisIndex < 1 || ParentHitObject.SlideCheckpoints[ThisIndex - 1].IsHit; private Container nodes = null!; @@ -60,7 +60,7 @@ protected override void OnApply() // Nodes are applied before being added to the parent playfield, so this node isn't in SlideNodes yet // Since we know that the node isn't in the container yet, and that the count is always one higher than the topmost element, we can use that as the predicted index - ThisIndex = parentSlide.SlideCheckpoints.Count; + ThisIndex = ParentHitObject.SlideCheckpoints.Count; } protected override void CheckForResult(bool userTriggered, double timeOffset)