diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs
index 915544d010..48d94d2b3f 100644
--- a/osu.Game/Rulesets/Ruleset.cs
+++ b/osu.Game/Rulesets/Ruleset.cs
@@ -23,8 +23,10 @@ using osu.Game.Scoring;
using osu.Game.Skinning;
using osu.Game.Users;
using JetBrains.Annotations;
+using osu.Framework.Extensions;
using osu.Framework.Testing;
using osu.Game.Screens.Ranking.Statistics;
+using osu.Game.Utils;
namespace osu.Game.Rulesets
{
@@ -220,5 +222,52 @@ namespace osu.Game.Rulesets
/// The s to display. Each may contain 0 or more .
[NotNull]
public virtual StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => Array.Empty();
+
+ ///
+ /// Get all valid s for this ruleset.
+ /// Generally used for results display purposes, where it can't be determined if zero-count means the user has not achieved any or the type is not used by this ruleset.
+ ///
+ ///
+ /// All valid s along with a display-friendly name.
+ ///
+ public IEnumerable<(HitResult result, string displayName)> GetHitResults()
+ {
+ var validResults = GetValidHitResults();
+
+ // enumerate over ordered list to guarantee return order is stable.
+ foreach (var result in OrderAttributeUtils.GetValuesInOrder())
+ {
+ switch (result)
+ {
+ // hard blocked types, should never be displayed even if the ruleset tells us to.
+ case HitResult.None:
+ case HitResult.IgnoreHit:
+ case HitResult.IgnoreMiss:
+ // display is handled as a completion count with corresponding "hit" type.
+ case HitResult.LargeTickMiss:
+ case HitResult.SmallTickMiss:
+ continue;
+ }
+
+ if (result == HitResult.Miss || validResults.Contains(result))
+ yield return (result, GetDisplayNameForHitResult(result));
+ }
+ }
+
+ ///
+ /// Get all valid s for this ruleset.
+ /// Generally used for results display purposes, where it can't be determined if zero-count means the user has not achieved any or the type is not used by this ruleset.
+ ///
+ ///
+ /// is implicitly included. Special types like are ignored even when specified.
+ ///
+ protected virtual IEnumerable GetValidHitResults() => OrderAttributeUtils.GetValuesInOrder();
+
+ ///
+ /// Get a display friendly name for the specified result type.
+ ///
+ /// The result type to get the name for.
+ /// The display name.
+ public virtual string GetDisplayNameForHitResult(HitResult result) => result.GetDescription();
}
}