2019-01-24 16:43:03 +08:00
|
|
|
// 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.
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2022-06-17 15:37:17 +08:00
|
|
|
#nullable disable
|
|
|
|
|
2021-08-26 03:19:49 +08:00
|
|
|
using System;
|
2017-03-15 15:07:40 +08:00
|
|
|
using System.ComponentModel;
|
2020-09-29 14:14:03 +08:00
|
|
|
using System.Diagnostics;
|
2021-08-26 03:19:49 +08:00
|
|
|
using System.Linq;
|
2022-03-08 16:58:37 +08:00
|
|
|
using System.Runtime.Serialization;
|
2021-01-28 05:01:56 +08:00
|
|
|
using osu.Framework.Utils;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-12-31 04:23:18 +08:00
|
|
|
namespace osu.Game.Rulesets.Scoring
|
2017-03-15 15:07:40 +08:00
|
|
|
{
|
2020-09-25 19:11:27 +08:00
|
|
|
[HasOrderedElements]
|
2017-03-15 15:07:40 +08:00
|
|
|
public enum HitResult
|
|
|
|
{
|
2017-03-29 16:57:36 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Indicates that the object has not been judged yet.
|
|
|
|
/// </summary>
|
2017-09-05 18:44:59 +08:00
|
|
|
[Description(@"")]
|
2022-03-08 16:58:37 +08:00
|
|
|
[EnumMember(Value = "none")]
|
2020-09-29 13:41:50 +08:00
|
|
|
[Order(14)]
|
2020-09-30 20:32:50 +08:00
|
|
|
None,
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-03-29 16:57:36 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Indicates that the object has been judged as a miss.
|
|
|
|
/// </summary>
|
2019-09-06 14:24:00 +08:00
|
|
|
/// <remarks>
|
|
|
|
/// This miss window should determine how early a hit can be before it is considered for judgement (as opposed to being ignored as
|
|
|
|
/// "too far in the future). It should also define when a forced miss should be triggered (as a result of no user input in time).
|
|
|
|
/// </remarks>
|
2017-03-15 15:07:40 +08:00
|
|
|
[Description(@"Miss")]
|
2022-03-08 16:58:37 +08:00
|
|
|
[EnumMember(Value = "miss")]
|
2020-09-25 19:11:27 +08:00
|
|
|
[Order(5)]
|
2020-09-30 20:32:50 +08:00
|
|
|
Miss,
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-09-05 18:44:59 +08:00
|
|
|
[Description(@"Meh")]
|
2022-03-08 16:58:37 +08:00
|
|
|
[EnumMember(Value = "meh")]
|
2020-09-25 19:11:27 +08:00
|
|
|
[Order(4)]
|
2017-09-05 18:44:59 +08:00
|
|
|
Meh,
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-09-05 18:44:59 +08:00
|
|
|
[Description(@"OK")]
|
2022-03-08 16:58:37 +08:00
|
|
|
[EnumMember(Value = "ok")]
|
2020-09-25 19:11:27 +08:00
|
|
|
[Order(3)]
|
2017-09-05 18:44:59 +08:00
|
|
|
Ok,
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-09-05 18:44:59 +08:00
|
|
|
[Description(@"Good")]
|
2022-03-08 16:58:37 +08:00
|
|
|
[EnumMember(Value = "good")]
|
2020-09-25 19:11:27 +08:00
|
|
|
[Order(2)]
|
2017-09-05 18:44:59 +08:00
|
|
|
Good,
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-09-05 18:44:59 +08:00
|
|
|
[Description(@"Great")]
|
2022-03-08 16:58:37 +08:00
|
|
|
[EnumMember(Value = "great")]
|
2020-09-25 19:11:27 +08:00
|
|
|
[Order(1)]
|
2017-09-05 18:44:59 +08:00
|
|
|
Great,
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-09-05 18:44:59 +08:00
|
|
|
[Description(@"Perfect")]
|
2022-03-08 16:58:37 +08:00
|
|
|
[EnumMember(Value = "perfect")]
|
2020-09-25 19:11:27 +08:00
|
|
|
[Order(0)]
|
2017-09-05 18:44:59 +08:00
|
|
|
Perfect,
|
2020-04-16 17:16:08 +08:00
|
|
|
|
2020-05-04 14:55:42 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Indicates small tick miss.
|
|
|
|
/// </summary>
|
2022-03-08 16:58:37 +08:00
|
|
|
[EnumMember(Value = "small_tick_miss")]
|
2020-09-25 19:11:27 +08:00
|
|
|
[Order(11)]
|
2020-09-30 20:32:50 +08:00
|
|
|
SmallTickMiss,
|
2020-05-04 14:55:42 +08:00
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Indicates a small tick hit.
|
|
|
|
/// </summary>
|
2020-09-25 19:11:27 +08:00
|
|
|
[Description(@"S Tick")]
|
2022-03-08 16:58:37 +08:00
|
|
|
[EnumMember(Value = "small_tick_hit")]
|
2020-09-25 19:11:27 +08:00
|
|
|
[Order(7)]
|
2020-05-04 14:55:42 +08:00
|
|
|
SmallTickHit,
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Indicates a large tick miss.
|
|
|
|
/// </summary>
|
2022-03-08 16:58:37 +08:00
|
|
|
[EnumMember(Value = "large_tick_miss")]
|
2020-09-25 19:11:27 +08:00
|
|
|
[Order(10)]
|
2020-09-30 20:32:50 +08:00
|
|
|
LargeTickMiss,
|
2020-05-04 14:55:42 +08:00
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Indicates a large tick hit.
|
|
|
|
/// </summary>
|
2020-09-25 19:11:27 +08:00
|
|
|
[Description(@"L Tick")]
|
2022-03-08 16:58:37 +08:00
|
|
|
[EnumMember(Value = "large_tick_hit")]
|
2020-09-25 19:11:27 +08:00
|
|
|
[Order(6)]
|
|
|
|
LargeTickHit,
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Indicates a small bonus.
|
|
|
|
/// </summary>
|
|
|
|
[Description("S Bonus")]
|
2022-03-08 16:58:37 +08:00
|
|
|
[EnumMember(Value = "small_bonus")]
|
2020-09-25 19:11:27 +08:00
|
|
|
[Order(9)]
|
2020-09-30 20:32:50 +08:00
|
|
|
SmallBonus,
|
2020-09-25 19:11:27 +08:00
|
|
|
|
|
|
|
/// <summary>
|
2020-09-29 15:32:50 +08:00
|
|
|
/// Indicates a large bonus.
|
2020-09-25 19:11:27 +08:00
|
|
|
/// </summary>
|
|
|
|
[Description("L Bonus")]
|
2022-03-08 16:58:37 +08:00
|
|
|
[EnumMember(Value = "large_bonus")]
|
2020-09-25 19:11:27 +08:00
|
|
|
[Order(8)]
|
2020-09-30 20:32:50 +08:00
|
|
|
LargeBonus,
|
2020-09-29 13:41:50 +08:00
|
|
|
|
2020-09-29 15:32:50 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Indicates a miss that should be ignored for scoring purposes.
|
|
|
|
/// </summary>
|
2022-03-08 16:58:37 +08:00
|
|
|
[EnumMember(Value = "ignore_miss")]
|
2020-09-29 13:41:50 +08:00
|
|
|
[Order(13)]
|
2020-09-30 20:32:50 +08:00
|
|
|
IgnoreMiss,
|
2020-09-29 13:41:50 +08:00
|
|
|
|
2020-09-29 15:32:50 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Indicates a hit that should be ignored for scoring purposes.
|
|
|
|
/// </summary>
|
2022-03-08 16:58:37 +08:00
|
|
|
[EnumMember(Value = "ignore_hit")]
|
2020-09-29 13:41:50 +08:00
|
|
|
[Order(12)]
|
|
|
|
IgnoreHit,
|
2022-08-23 20:00:30 +08:00
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// A special result used as a padding value for legacy rulesets. It is a hit type and affects combo, but does not contribute to score.
|
|
|
|
/// </summary>
|
|
|
|
/// <remarks>
|
|
|
|
/// DO NOT USE.
|
|
|
|
/// </remarks>
|
|
|
|
[EnumMember(Value = "legacy_combo_increase")]
|
|
|
|
[Order(99)]
|
|
|
|
[Obsolete("Do not use.")]
|
|
|
|
LegacyComboIncrease = 99
|
2020-09-25 19:11:27 +08:00
|
|
|
}
|
|
|
|
|
2022-08-23 20:00:30 +08:00
|
|
|
#pragma warning disable CS0618
|
2020-09-25 19:11:27 +08:00
|
|
|
public static class HitResultExtensions
|
|
|
|
{
|
|
|
|
/// <summary>
|
2022-03-18 20:22:00 +08:00
|
|
|
/// Whether a <see cref="HitResult"/> increases the combo.
|
2020-09-25 19:11:27 +08:00
|
|
|
/// </summary>
|
2022-03-18 20:22:00 +08:00
|
|
|
public static bool IncreasesCombo(this HitResult result)
|
2022-03-18 22:18:42 +08:00
|
|
|
=> AffectsCombo(result) && IsHit(result);
|
2022-03-18 20:22:00 +08:00
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Whether a <see cref="HitResult"/> breaks the combo and resets it back to zero.
|
|
|
|
/// </summary>
|
|
|
|
public static bool BreaksCombo(this HitResult result)
|
2022-03-18 22:18:42 +08:00
|
|
|
=> AffectsCombo(result) && !IsHit(result);
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Whether a <see cref="HitResult"/> increases/breaks the combo, and affects the combo portion of the score.
|
|
|
|
/// </summary>
|
|
|
|
public static bool AffectsCombo(this HitResult result)
|
2022-03-18 20:22:00 +08:00
|
|
|
{
|
|
|
|
switch (result)
|
|
|
|
{
|
|
|
|
case HitResult.Miss:
|
2022-03-18 22:18:42 +08:00
|
|
|
case HitResult.Meh:
|
|
|
|
case HitResult.Ok:
|
|
|
|
case HitResult.Good:
|
|
|
|
case HitResult.Great:
|
|
|
|
case HitResult.Perfect:
|
|
|
|
case HitResult.LargeTickHit:
|
2020-09-25 19:11:27 +08:00
|
|
|
case HitResult.LargeTickMiss:
|
2022-08-23 20:00:30 +08:00
|
|
|
case HitResult.LegacyComboIncrease:
|
2020-09-25 19:11:27 +08:00
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
2020-09-25 21:16:14 +08:00
|
|
|
/// Whether a <see cref="HitResult"/> affects the accuracy portion of the score.
|
|
|
|
/// </summary>
|
|
|
|
public static bool AffectsAccuracy(this HitResult result)
|
2022-08-23 20:00:30 +08:00
|
|
|
{
|
|
|
|
if (result == HitResult.LegacyComboIncrease)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return IsScorable(result) && !IsBonus(result);
|
|
|
|
}
|
2020-09-25 21:16:14 +08:00
|
|
|
|
2022-03-08 17:19:11 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Whether a <see cref="HitResult"/> is a non-tick and non-bonus result.
|
|
|
|
/// </summary>
|
|
|
|
public static bool IsBasic(this HitResult result)
|
2022-08-23 20:00:30 +08:00
|
|
|
{
|
|
|
|
if (result == HitResult.LegacyComboIncrease)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return IsScorable(result) && !IsTick(result) && !IsBonus(result);
|
|
|
|
}
|
2022-03-08 17:19:11 +08:00
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Whether a <see cref="HitResult"/> should be counted as a tick.
|
|
|
|
/// </summary>
|
|
|
|
public static bool IsTick(this HitResult result)
|
|
|
|
{
|
|
|
|
switch (result)
|
|
|
|
{
|
|
|
|
case HitResult.LargeTickHit:
|
|
|
|
case HitResult.LargeTickMiss:
|
|
|
|
case HitResult.SmallTickHit:
|
|
|
|
case HitResult.SmallTickMiss:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-25 21:16:14 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Whether a <see cref="HitResult"/> should be counted as bonus score.
|
2020-09-25 19:11:27 +08:00
|
|
|
/// </summary>
|
|
|
|
public static bool IsBonus(this HitResult result)
|
|
|
|
{
|
|
|
|
switch (result)
|
|
|
|
{
|
|
|
|
case HitResult.SmallBonus:
|
|
|
|
case HitResult.LargeBonus:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Whether a <see cref="HitResult"/> represents a successful hit.
|
|
|
|
/// </summary>
|
|
|
|
public static bool IsHit(this HitResult result)
|
|
|
|
{
|
|
|
|
switch (result)
|
|
|
|
{
|
|
|
|
case HitResult.None:
|
2020-09-29 13:41:50 +08:00
|
|
|
case HitResult.IgnoreMiss:
|
2020-09-25 19:11:27 +08:00
|
|
|
case HitResult.Miss:
|
|
|
|
case HitResult.SmallTickMiss:
|
|
|
|
case HitResult.LargeTickMiss:
|
|
|
|
return false;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Whether a <see cref="HitResult"/> is scorable.
|
|
|
|
/// </summary>
|
2022-08-23 20:00:30 +08:00
|
|
|
public static bool IsScorable(this HitResult result)
|
|
|
|
{
|
|
|
|
if (result == HitResult.LegacyComboIncrease)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return result >= HitResult.Miss && result < HitResult.IgnoreMiss;
|
|
|
|
}
|
2020-09-29 14:14:03 +08:00
|
|
|
|
2021-08-26 03:19:49 +08:00
|
|
|
/// <summary>
|
|
|
|
/// An array of all scorable <see cref="HitResult"/>s.
|
|
|
|
/// </summary>
|
2022-08-23 20:00:30 +08:00
|
|
|
public static readonly HitResult[] ALL_TYPES = ((HitResult[])Enum.GetValues(typeof(HitResult))).Except(new[] { HitResult.LegacyComboIncrease }).ToArray();
|
2021-08-26 03:19:49 +08:00
|
|
|
|
2020-09-29 14:14:03 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Whether a <see cref="HitResult"/> is valid within a given <see cref="HitResult"/> range.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="result">The <see cref="HitResult"/> to check.</param>
|
|
|
|
/// <param name="minResult">The minimum <see cref="HitResult"/>.</param>
|
|
|
|
/// <param name="maxResult">The maximum <see cref="HitResult"/>.</param>
|
|
|
|
/// <returns>Whether <see cref="HitResult"/> falls between <paramref name="minResult"/> and <paramref name="maxResult"/>.</returns>
|
|
|
|
public static bool IsValidHitResult(this HitResult result, HitResult minResult, HitResult maxResult)
|
|
|
|
{
|
|
|
|
if (result == HitResult.None)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (result == minResult || result == maxResult)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
Debug.Assert(minResult <= maxResult);
|
|
|
|
return result > minResult && result < maxResult;
|
|
|
|
}
|
2017-03-15 15:07:40 +08:00
|
|
|
}
|
2022-08-23 20:00:30 +08:00
|
|
|
#pragma warning restore CS0618
|
2017-09-05 18:44:59 +08:00
|
|
|
}
|