// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Runtime.Serialization; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Utils; namespace osu.Game.Rulesets.Scoring { [HasOrderedElements] public enum HitResult { /// /// Indicates that the object has not been judged yet. /// [Description(@"")] [EnumMember(Value = "none")] [Order(14)] None, /// /// Indicates that the object has been judged as a miss. /// /// /// 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). /// [Description(@"Miss")] [EnumMember(Value = "miss")] [Order(5)] Miss, [Description(@"Meh")] [EnumMember(Value = "meh")] [Order(4)] Meh, [Description(@"OK")] [EnumMember(Value = "ok")] [Order(3)] Ok, [Description(@"Good")] [EnumMember(Value = "good")] [Order(2)] Good, [Description(@"Great")] [EnumMember(Value = "great")] [Order(1)] Great, [Description(@"Perfect")] [EnumMember(Value = "perfect")] [Order(0)] Perfect, /// /// Indicates small tick miss. /// [EnumMember(Value = "small_tick_miss")] [Order(11)] SmallTickMiss, /// /// Indicates a small tick hit. /// [Description(@"S Tick")] [EnumMember(Value = "small_tick_hit")] [Order(7)] SmallTickHit, /// /// Indicates a large tick miss. /// [EnumMember(Value = "large_tick_miss")] [Order(10)] LargeTickMiss, /// /// Indicates a large tick hit. /// [Description(@"L Tick")] [EnumMember(Value = "large_tick_hit")] [Order(6)] LargeTickHit, /// /// Indicates a small bonus. /// [Description("S Bonus")] [EnumMember(Value = "small_bonus")] [Order(9)] SmallBonus, /// /// Indicates a large bonus. /// [Description("L Bonus")] [EnumMember(Value = "large_bonus")] [Order(8)] LargeBonus, /// /// Indicates a miss that should be ignored for scoring purposes. /// [EnumMember(Value = "ignore_miss")] [Order(13)] IgnoreMiss, /// /// Indicates a hit that should be ignored for scoring purposes. /// [EnumMember(Value = "ignore_hit")] [Order(12)] IgnoreHit, /// /// A special result used as a padding value for legacy rulesets. It is a hit type and affects combo, but does not affect the base score (does not affect accuracy). /// /// /// DO NOT USE. /// [EnumMember(Value = "legacy_combo_increase")] [Order(99)] [Obsolete("Do not use.")] LegacyComboIncrease = 99 } #pragma warning disable CS0618 public static class HitResultExtensions { private static readonly IList order = EnumExtensions.GetValuesInOrder().ToList(); /// /// Whether a increases the combo. /// public static bool IncreasesCombo(this HitResult result) => AffectsCombo(result) && IsHit(result); /// /// Whether a breaks the combo and resets it back to zero. /// public static bool BreaksCombo(this HitResult result) => AffectsCombo(result) && !IsHit(result); /// /// Whether a increases/breaks the combo, and affects the combo portion of the score. /// public static bool AffectsCombo(this HitResult result) { switch (result) { case HitResult.Miss: case HitResult.Meh: case HitResult.Ok: case HitResult.Good: case HitResult.Great: case HitResult.Perfect: case HitResult.LargeTickHit: case HitResult.LargeTickMiss: case HitResult.LegacyComboIncrease: return true; default: return false; } } /// /// Whether a affects the accuracy portion of the score. /// public static bool AffectsAccuracy(this HitResult result) { // LegacyComboIncrease is a special type which is neither a basic, tick, bonus, or accuracy-affecting result. if (result == HitResult.LegacyComboIncrease) return false; return IsScorable(result) && !IsBonus(result); } /// /// Whether a is a non-tick and non-bonus result. /// public static bool IsBasic(this HitResult result) { // LegacyComboIncrease is a special type which is neither a basic, tick, bonus, or accuracy-affecting result. if (result == HitResult.LegacyComboIncrease) return false; return IsScorable(result) && !IsTick(result) && !IsBonus(result); } /// /// Whether a should be counted as a tick. /// 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; } } /// /// Whether a should be counted as bonus score. /// public static bool IsBonus(this HitResult result) { switch (result) { case HitResult.SmallBonus: case HitResult.LargeBonus: return true; default: return false; } } /// /// Whether a represents a successful hit. /// public static bool IsHit(this HitResult result) { switch (result) { case HitResult.None: case HitResult.IgnoreMiss: case HitResult.Miss: case HitResult.SmallTickMiss: case HitResult.LargeTickMiss: return false; default: return true; } } /// /// Whether a is scorable. /// public static bool IsScorable(this HitResult result) { // LegacyComboIncrease is not actually scorable (in terms of usable by rulesets for that purpose), but needs to be defined as such to be correctly included in statistics output. if (result == HitResult.LegacyComboIncrease) return true; return result >= HitResult.Miss && result < HitResult.IgnoreMiss; } /// /// An array of all scorable s. /// public static readonly HitResult[] ALL_TYPES = ((HitResult[])Enum.GetValues(typeof(HitResult))).Except(new[] { HitResult.LegacyComboIncrease }).ToArray(); /// /// Whether a is valid within a given range. /// /// The to check. /// The minimum . /// The maximum . /// Whether falls between and . 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; } /// /// Ordered index of a . Used for consistent order when displaying hit results to the user. /// /// The to get the index of. /// The index of . public static int GetIndexForOrderedDisplay(this HitResult result) => order.IndexOf(result); } #pragma warning restore CS0618 }