1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 11:20:04 +08:00

Separate into separate class

This commit is contained in:
smoogipoo 2020-04-10 02:02:09 +09:00
parent ea1bec85ae
commit 10e849d196
4 changed files with 111 additions and 73 deletions

View File

@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
var result = HitObject.HitWindows.ResultFor(timeOffset);
if (result == HitResult.None || CheckHittable?.Invoke(this) == false)
if (result == HitResult.None || CheckHittable?.Invoke(this, Time.Current) == false)
{
Shake(Math.Abs(timeOffset) - HitObject.HitWindows.WindowFor(HitResult.Miss));
return;

View File

@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
/// Whether this <see cref="DrawableOsuHitObject"/> can be hit.
/// If non-null, judgements will be ignored (resulting in a shake) whilst the function returns false.
/// </summary>
public Func<DrawableOsuHitObject, bool> CheckHittable;
public Func<DrawableHitObject, double, bool> CheckHittable;
protected DrawableOsuHitObject(OsuHitObject hitObject)
: base(hitObject)

View File

@ -0,0 +1,104 @@
// 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 osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Osu.UI
{
/// <summary>
/// Ensures that <see cref="HitObject"/>s are hit in-order.
/// If a <see cref="HitObject"/> is hit out of order:
/// <list type="number">
/// <item><description>The hit is blocked if it occurred earlier than the previous <see cref="HitObject"/>'s start time.</description></item>
/// <item><description>The hit causes all previous <see cref="HitObject"/>s to missed otherwise.</description></item>
/// </list>
/// </summary>
public class OrderedHitPolicy
{
private readonly HitObjectContainer hitObjectContainer;
public OrderedHitPolicy(HitObjectContainer hitObjectContainer)
{
this.hitObjectContainer = hitObjectContainer;
}
/// <summary>
/// Determines whether a <see cref="DrawableHitObject"/> can be hit at a point in time.
/// </summary>
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to check.</param>
/// <param name="time">The time to check.</param>
/// <returns>Whether <paramref name="hitObject"/> can be hit at the given <paramref name="time"/>.</returns>
public bool IsHittable(DrawableHitObject hitObject, double time)
{
DrawableHitObject lastObject = hitObject;
// Get the last hitobject that can block future hits
while ((lastObject = hitObjectContainer.AliveObjects.GetPrevious(lastObject)) != null)
{
if (canBlockFutureHits(lastObject.HitObject))
break;
}
// If there is no previous object alive, allow the hit.
if (lastObject == null)
return true;
// Ensure that either the last object has received a judgement or the hit time occurs at or after the last object's start time.
// Simultaneous hitobjects are allowed to be hit at the same time value to account for edge-cases such as Centipede.
if (lastObject.Judged || time >= lastObject.HitObject.StartTime)
return true;
return false;
}
/// <summary>
/// Handles a <see cref="HitObject"/> being hit to potentially miss all earlier <see cref="HitObject"/>s.
/// </summary>
/// <param name="hitObject">The <see cref="HitObject"/> that was hit.</param>
public void HandleHit(HitObject hitObject)
{
if (!canBlockFutureHits(hitObject))
return;
double minimumTime = hitObject.StartTime;
foreach (var obj in hitObjectContainer.AliveObjects)
{
if (obj.HitObject.StartTime >= minimumTime)
break;
switch (obj)
{
case DrawableHitCircle circle:
miss(circle);
break;
case DrawableSlider slider:
miss(slider.HeadCircle);
break;
}
}
static void miss(DrawableOsuHitObject obj)
{
// Hitobjects that have already been judged cannot be missed.
if (obj.Judged)
return;
obj.MissForcefully();
}
}
/// <summary>
/// Whether a <see cref="HitObject"/> blocks hits on future <see cref="HitObject"/>s until its start time is reached.
/// </summary>
/// <param name="hitObject">The <see cref="HitObject"/> to test.</param>
private bool canBlockFutureHits(HitObject hitObject)
=> hitObject is HitCircle || hitObject is Slider;
}
}

View File

@ -1,7 +1,6 @@
// 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 osu.Framework.Extensions.IEnumerableExtensions;
using osuTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -11,7 +10,6 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Connections;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Game.Skinning;
@ -22,6 +20,7 @@ namespace osu.Game.Rulesets.Osu.UI
private readonly ApproachCircleProxyContainer approachCircles;
private readonly JudgementContainer<DrawableOsuJudgement> judgementLayer;
private readonly FollowPointRenderer followPoints;
private readonly OrderedHitPolicy hitPolicy;
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
@ -53,6 +52,8 @@ namespace osu.Game.Rulesets.Osu.UI
Depth = -1,
},
};
hitPolicy = new OrderedHitPolicy(HitObjectContainer);
}
public override void Add(DrawableHitObject h)
@ -67,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.UI
base.Add(h);
DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h;
osuHitObject.CheckHittable = checkHittable;
osuHitObject.CheckHittable = hitPolicy.IsHittable;
followPoints.AddFollowPoints(osuHitObject);
}
@ -82,34 +83,10 @@ namespace osu.Game.Rulesets.Osu.UI
return result;
}
private bool checkHittable(DrawableOsuHitObject osuHitObject)
{
DrawableHitObject lastObject = osuHitObject;
// Get the last hitobject that can block future hits
while ((lastObject = HitObjectContainer.AliveObjects.GetPrevious(lastObject)) != null)
{
if (canBlockFutureHits(lastObject.HitObject))
break;
}
// If there is no previous object alive, allow the hit.
if (lastObject == null)
return true;
// Ensure that either the last object has received a judgement or the hit time occurs at or after the last object's start time.
// Simultaneous hitobjects are allowed to be hit at the same time value to account for edge-cases such as Centipede.
if (lastObject.Judged || Time.Current >= lastObject.HitObject.StartTime)
return true;
return false;
}
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
// Hitobjects that block future hits should miss previous hitobjects if they're hit out-of-order.
if (canBlockFutureHits(result.HitObject))
missAllEarlierObjects(result.HitObject);
hitPolicy.HandleHit(result.HitObject);
if (!judgedObject.DisplayResult || !DisplayJudgements.Value)
return;
@ -124,49 +101,6 @@ 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="hitObject">The marker <see cref="HitObject"/>, which all <see cref="HitObject"/>s earlier than will get missed.</param>
private void missAllEarlierObjects(HitObject hitObject)
{
double minimumTime = hitObject.StartTime;
foreach (var obj in HitObjectContainer.AliveObjects)
{
if (obj.HitObject.StartTime >= minimumTime)
break;
switch (obj)
{
case DrawableHitCircle circle:
miss(circle);
break;
case DrawableSlider slider:
miss(slider.HeadCircle);
break;
}
}
static void miss(DrawableOsuHitObject obj)
{
// Hitobjects that have already been judged cannot be missed.
if (obj.Judged)
return;
obj.MissForcefully();
}
}
/// <summary>
/// Whether a <see cref="HitObject"/> can block hits on future <see cref="HitObject"/>s until its start time is reached.
/// </summary>
/// <param name="hitObject">The <see cref="HitObject"/> to test.</param>
/// <returns>Whether <paramref name="hitObject"/> can block hits on future <see cref="HitObject"/>s.</returns>
private bool canBlockFutureHits(HitObject hitObject)
=> hitObject is HitCircle || hitObject is Slider;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => HitObjectContainer.ReceivePositionalInputAt(screenSpacePos);
private class ApproachCircleProxyContainer : LifetimeManagementContainer