1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-13 08:32:57 +08:00

Cause all earlier hitobjects to get missed

This commit is contained in:
smoogipoo 2020-03-19 19:16:24 +09:00
parent f285b43a74
commit 12a48d2774
3 changed files with 70 additions and 1 deletions

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Screens;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
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_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_second_circle = new Vector2(80);
@ -40,6 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests
});
addJudgementAssert(HitResult.Miss, HitResult.Miss);
addJudgementOffsetAssert(late_miss_window);
}
/// <summary>
@ -54,6 +58,7 @@ namespace osu.Game.Rulesets.Osu.Tests
});
addJudgementAssert(HitResult.Miss, HitResult.Great);
addJudgementOffsetAssert(0);
}
/// <summary>
@ -68,6 +73,7 @@ namespace osu.Game.Rulesets.Osu.Tests
});
addJudgementAssert(HitResult.Miss, HitResult.Great);
addJudgementOffsetAssert(100);
}
/// <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);
}
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 List<JudgementResult> judgementResults;
private bool allJudgedFired;
@ -157,7 +168,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private static readonly DifficultyRange[] ranges =
{
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;

View File

@ -7,6 +7,7 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Scoring;
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);
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Extensions.IEnumerableExtensions;
using osuTK;
using osu.Framework.Graphics;
@ -104,6 +105,8 @@ namespace osu.Game.Rulesets.Osu.UI
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
missAllEarlier(result);
if (!judgedObject.DisplayResult || !DisplayJudgements.Value)
return;
@ -117,6 +120,55 @@ namespace osu.Game.Rulesets.Osu.UI
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);
private class ApproachCircleProxyContainer : LifetimeManagementContainer