mirror of
https://github.com/ppy/osu
synced 2025-02-19 11:56:58 +00:00
Cause all earlier hitobjects to get missed
This commit is contained in:
parent
f285b43a74
commit
12a48d2774
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
@ -24,6 +25,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
private const double time_first_circle = 1500;
|
private const double time_first_circle = 1500;
|
||||||
private const double time_second_circle = 1600;
|
private const double time_second_circle = 1600;
|
||||||
|
private const double early_miss_window = 1000; // time after -1000 to -500 is considered a miss
|
||||||
|
private const double late_miss_window = 500; // time after +500 is considered a miss
|
||||||
|
|
||||||
private static readonly Vector2 position_first_circle = Vector2.Zero;
|
private static readonly Vector2 position_first_circle = Vector2.Zero;
|
||||||
private static readonly Vector2 position_second_circle = new Vector2(80);
|
private static readonly Vector2 position_second_circle = new Vector2(80);
|
||||||
@ -40,6 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
});
|
});
|
||||||
|
|
||||||
addJudgementAssert(HitResult.Miss, HitResult.Miss);
|
addJudgementAssert(HitResult.Miss, HitResult.Miss);
|
||||||
|
addJudgementOffsetAssert(late_miss_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -54,6 +58,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
});
|
});
|
||||||
|
|
||||||
addJudgementAssert(HitResult.Miss, HitResult.Great);
|
addJudgementAssert(HitResult.Miss, HitResult.Great);
|
||||||
|
addJudgementOffsetAssert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -68,6 +73,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
});
|
});
|
||||||
|
|
||||||
addJudgementAssert(HitResult.Miss, HitResult.Great);
|
addJudgementAssert(HitResult.Miss, HitResult.Great);
|
||||||
|
addJudgementOffsetAssert(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -91,6 +97,11 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
AddAssert($"second circle judgement is {secondCircle}", () => judgementResults.Single(r => r.HitObject.StartTime == time_second_circle).Type == secondCircle);
|
AddAssert($"second circle judgement is {secondCircle}", () => judgementResults.Single(r => r.HitObject.StartTime == time_second_circle).Type == secondCircle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addJudgementOffsetAssert(double offset)
|
||||||
|
{
|
||||||
|
AddAssert($"first circle judged at {offset}", () => Precision.AlmostEquals(judgementResults.Single(r => r.HitObject.StartTime == time_first_circle).TimeOffset, offset, 100));
|
||||||
|
}
|
||||||
|
|
||||||
private ScoreAccessibleReplayPlayer currentPlayer;
|
private ScoreAccessibleReplayPlayer currentPlayer;
|
||||||
private List<JudgementResult> judgementResults;
|
private List<JudgementResult> judgementResults;
|
||||||
private bool allJudgedFired;
|
private bool allJudgedFired;
|
||||||
@ -157,7 +168,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
private static readonly DifficultyRange[] ranges =
|
private static readonly DifficultyRange[] ranges =
|
||||||
{
|
{
|
||||||
new DifficultyRange(HitResult.Great, 500, 500, 500),
|
new DifficultyRange(HitResult.Great, 500, 500, 500),
|
||||||
new DifficultyRange(HitResult.Miss, 1000, 1000, 1000),
|
new DifficultyRange(HitResult.Miss, early_miss_window, early_miss_window, early_miss_window),
|
||||||
};
|
};
|
||||||
|
|
||||||
public override bool IsHitResultAllowed(HitResult result) => result == HitResult.Great || result == HitResult.Miss;
|
public override bool IsHitResultAllowed(HitResult result) => result == HitResult.Great || result == HitResult.Miss;
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
{
|
{
|
||||||
@ -61,6 +62,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Causes this <see cref="DrawableOsuHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
|
||||||
|
/// </summary>
|
||||||
|
public void MissForcefully() => ApplyResult(r => r.Type = HitResult.Miss);
|
||||||
|
|
||||||
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(HitObject, judgement);
|
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(HitObject, judgement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -104,6 +105,8 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
|
|
||||||
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||||
{
|
{
|
||||||
|
missAllEarlier(result);
|
||||||
|
|
||||||
if (!judgedObject.DisplayResult || !DisplayJudgements.Value)
|
if (!judgedObject.DisplayResult || !DisplayJudgements.Value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -117,6 +120,55 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
judgementLayer.Add(explosion);
|
judgementLayer.Add(explosion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Misses all <see cref="OsuHitObject"/>s occurring earlier than the start time of a judged <see cref="OsuHitObject"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">The <see cref="JudgementResult"/> of the judged <see cref="OsuHitObject"/>.</param>
|
||||||
|
private void missAllEarlier(JudgementResult result)
|
||||||
|
{
|
||||||
|
// Hitobjects that count as bonus should not cause other hitobjects to get missed.
|
||||||
|
// E.g. For the sequence slider-head -> circle -> slider-tick, hitting the tick before the circle should not cause the circle to be missed.
|
||||||
|
// E.g. For the sequence spinner -> circle -> spinner-bonus, hitting the bonus before the circle should not cause the circle to be missed.
|
||||||
|
if (result.Judgement.IsBonus)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The minimum start time required for hitobjects so that they aren't missed.
|
||||||
|
double minimumTime = result.HitObject.StartTime;
|
||||||
|
|
||||||
|
foreach (var obj in HitObjectContainer.AliveObjects)
|
||||||
|
{
|
||||||
|
if (obj.HitObject.StartTime >= minimumTime)
|
||||||
|
break;
|
||||||
|
|
||||||
|
attemptMiss(obj);
|
||||||
|
|
||||||
|
foreach (var n in obj.NestedHitObjects)
|
||||||
|
{
|
||||||
|
if (n.HitObject.StartTime >= minimumTime)
|
||||||
|
break;
|
||||||
|
|
||||||
|
attemptMiss(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void attemptMiss(DrawableHitObject obj)
|
||||||
|
{
|
||||||
|
if (!(obj is DrawableOsuHitObject osuObject))
|
||||||
|
throw new InvalidOperationException($"{obj.GetType()} is not a {nameof(DrawableOsuHitObject)}.");
|
||||||
|
|
||||||
|
// Hitobjects that have already been judged cannot be missed.
|
||||||
|
if (osuObject.Judged)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Hitobjects that count as bonus should not be missed.
|
||||||
|
// For the sequence slider-head -> slider-tick -> circle, hitting the circle before the tick should not cause the tick to be missed.
|
||||||
|
if (osuObject.Result.Judgement.IsBonus)
|
||||||
|
return;
|
||||||
|
|
||||||
|
osuObject.MissForcefully();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => HitObjectContainer.ReceivePositionalInputAt(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => HitObjectContainer.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
private class ApproachCircleProxyContainer : LifetimeManagementContainer
|
private class ApproachCircleProxyContainer : LifetimeManagementContainer
|
||||||
|
Loading…
Reference in New Issue
Block a user