Skip to content

Commit

Permalink
Merge pull request #22499 from ekrctb/catch-hyperdash-stable-sort
Browse files Browse the repository at this point in the history
Use stable sort for catch hyperdash generation
  • Loading branch information
smoogipoo authored Dec 13, 2023
2 parents 36d0ec3 + c255339 commit b4d0bcc
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 37 deletions.
20 changes: 15 additions & 5 deletions osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public void TestHyperDash()

AddAssert("First note is hyperdash", () => Beatmap.Value.Beatmap.HitObjects[0] is Fruit f && f.HyperDash);

for (int i = 0; i < 9; i++)
for (int i = 0; i < 11; i++)
{
int count = i + 1;
AddUntilStep($"wait for hyperdash #{count}", () => hyperDashCount >= count);
Expand Down Expand Up @@ -104,20 +104,30 @@ protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
})
}, 1);

createObjects(() => new Fruit { X = right_x }, count: 2, spacing: 0, spacingAfterGroup: 400);
createObjects(() => new TestJuiceStream(left_x)
{
Path = new SliderPath(new[]
{
new PathControlPoint(Vector2.Zero),
new PathControlPoint(new Vector2(0, 300))
})
}, count: 1, spacingAfterGroup: 150);
createObjects(() => new Fruit { X = left_x }, count: 1, spacing: 0, spacingAfterGroup: 400);
createObjects(() => new Fruit { X = right_x }, count: 2, spacing: 0);

return beatmap;

void createObjects(Func<CatchHitObject> createObject, int count = 3)
void createObjects(Func<CatchHitObject> createObject, int count = 3, float spacing = 140, float spacingAfterGroup = 700)
{
const float spacing = 140;

for (int i = 0; i < count; i++)
{
var hitObject = createObject();
hitObject.StartTime = startTime + i * spacing;
beatmap.HitObjects.Add(hitObject);
}

startTime += 700;
startTime += spacingAfterGroup;
}
}

Expand Down
21 changes: 21 additions & 0 deletions osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Objects;

namespace osu.Game.Rulesets.Catch.Beatmaps
{
Expand Down Expand Up @@ -38,5 +39,25 @@ public override IEnumerable<BeatmapStatistic> GetStatistics()
}
};
}

/// <summary>
/// Enumerate all <see cref="PalpableCatchHitObject"/>s, sorted by their start times.
/// </summary>
/// <remarks>
/// If multiple objects have the same start time, the ordering is preserved (it is a stable sorting).
/// </remarks>
public static IEnumerable<PalpableCatchHitObject> GetPalpableObjects(IEnumerable<HitObject> hitObjects)
{
return hitObjects.SelectMany(selectPalpableObjects).OrderBy(h => h.StartTime);

IEnumerable<PalpableCatchHitObject> selectPalpableObjects(HitObject h)
{
if (h is PalpableCatchHitObject palpable)
yield return palpable;

foreach (var nested in h.NestedHitObjects.OfType<PalpableCatchHitObject>())
yield return nested;
}
}
}
}
24 changes: 4 additions & 20 deletions osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
Expand Down Expand Up @@ -208,24 +207,9 @@ private static void applyOffset(ref float position, float amount)

private static void initialiseHyperDash(IBeatmap beatmap)
{
List<PalpableCatchHitObject> palpableObjects = new List<PalpableCatchHitObject>();

foreach (var currentObject in beatmap.HitObjects)
{
if (currentObject is Fruit fruitObject)
palpableObjects.Add(fruitObject);

if (currentObject is JuiceStream)
{
foreach (var juice in currentObject.NestedHitObjects)
{
if (juice is PalpableCatchHitObject palpableObject && !(juice is TinyDroplet))
palpableObjects.Add(palpableObject);
}
}
}

palpableObjects.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime));
var palpableObjects = CatchBeatmap.GetPalpableObjects(beatmap.HitObjects)
.Where(h => h is Fruit || (h is Droplet && h is not TinyDroplet))
.ToArray();

double halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.Difficulty) / 2;

Expand All @@ -237,7 +221,7 @@ private static void initialiseHyperDash(IBeatmap beatmap)
int lastDirection = 0;
double lastExcess = halfCatcherWidth;

for (int i = 0; i < palpableObjects.Count - 1; i++)
for (int i = 0; i < palpableObjects.Length - 1; i++)
{
var currentObject = palpableObjects[i];
var nextObject = palpableObjects[i + 1];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Difficulty.Preprocessing;
using osu.Game.Rulesets.Catch.Difficulty.Skills;
using osu.Game.Rulesets.Catch.Mods;
Expand Down Expand Up @@ -56,13 +57,10 @@ protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(I
List<DifficultyHitObject> objects = new List<DifficultyHitObject>();

// In 2B beatmaps, it is possible that a normal Fruit is placed in the middle of a JuiceStream.
foreach (var hitObject in beatmap.HitObjects
.SelectMany(obj => obj is JuiceStream stream ? stream.NestedHitObjects.AsEnumerable() : new[] { obj })
.Cast<CatchHitObject>()
.OrderBy(x => x.StartTime))
foreach (var hitObject in CatchBeatmap.GetPalpableObjects(beatmap.HitObjects))
{
// We want to only consider fruits that contribute to the combo.
if (hitObject is BananaShower || hitObject is TinyDroplet)
if (hitObject is Banana || hitObject is TinyDroplet)
continue;

if (lastObject != null)
Expand Down
26 changes: 19 additions & 7 deletions osu.Game.Rulesets.Catch/UI/Catcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ public CatcherAnimationState CurrentState

private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR;

private double? lastHyperDashStartTime;
private double hyperDashModifier = 1;
private int hyperDashDirection;
private float hyperDashTargetPosition;
Expand Down Expand Up @@ -233,16 +234,23 @@ public void OnNewResult(DrawableCatchHitObject drawableObject, JudgementResult r
// droplet doesn't affect the catcher state
if (hitObject is TinyDroplet) return;

if (result.IsHit && hitObject.HyperDashTarget is CatchHitObject target)
// if a hyper fruit was already handled this frame, just go where it says to go.
// this special-cases some aspire maps that have doubled-up objects (one hyper, one not) at the same time instant.
// handling this "properly" elsewhere is impossible as there is no feasible way to ensure
// that the hyperfruit gets judged second (especially if it coincides with a last fruit in a juice stream).
if (lastHyperDashStartTime != Time.Current)
{
double timeDifference = target.StartTime - hitObject.StartTime;
double positionDifference = target.EffectiveX - X;
double velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0);
if (result.IsHit && hitObject.HyperDashTarget is CatchHitObject target)
{
double timeDifference = target.StartTime - hitObject.StartTime;
double positionDifference = target.EffectiveX - X;
double velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0);

SetHyperDashState(Math.Abs(velocity) / BASE_DASH_SPEED, target.EffectiveX);
SetHyperDashState(Math.Abs(velocity) / BASE_DASH_SPEED, target.EffectiveX);
}
else
SetHyperDashState();
}
else
SetHyperDashState();

if (result.IsHit)
CurrentState = hitObject.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle;
Expand Down Expand Up @@ -292,6 +300,8 @@ public void SetHyperDashState(double modifier = 1, float targetPosition = -1)

if (wasHyperDashing)
runHyperDashStateTransition(false);

lastHyperDashStartTime = null;
}
else
{
Expand All @@ -301,6 +311,8 @@ public void SetHyperDashState(double modifier = 1, float targetPosition = -1)

if (!wasHyperDashing)
runHyperDashStateTransition(true);

lastHyperDashStartTime = Time.Current;
}
}

Expand Down

0 comments on commit b4d0bcc

Please sign in to comment.