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
using System ;
using System.Collections.Generic ;
using System.Linq ;
using osu.Framework.Graphics ;
2019-03-27 18:29:27 +08:00
using osu.Framework.Graphics.Sprites ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Input.Bindings ;
2019-09-04 19:28:21 +08:00
using osu.Framework.IO.Stores ;
2018-04-13 17:19:50 +08:00
using osu.Game.Beatmaps ;
using osu.Game.Overlays.Settings ;
using osu.Game.Rulesets.Edit ;
using osu.Game.Rulesets.Mods ;
using osu.Game.Rulesets.Replays.Types ;
using osu.Game.Rulesets.UI ;
2018-04-13 21:41:35 +08:00
using osu.Game.Beatmaps.Legacy ;
2018-06-11 12:17:08 +08:00
using osu.Game.Configuration ;
using osu.Game.Rulesets.Configuration ;
2018-05-15 16:38:04 +08:00
using osu.Game.Rulesets.Difficulty ;
2019-12-17 19:08:13 +08:00
using osu.Game.Rulesets.Scoring ;
2018-11-28 15:12:57 +08:00
using osu.Game.Scoring ;
2019-08-26 11:21:49 +08:00
using osu.Game.Skinning ;
2020-01-03 19:39:15 +08:00
using osu.Game.Users ;
2020-06-02 19:32:52 +08:00
using JetBrains.Annotations ;
2020-10-07 14:34:23 +08:00
using osu.Framework.Extensions ;
2021-01-28 05:01:56 +08:00
using osu.Framework.Extensions.EnumExtensions ;
2020-09-04 19:34:26 +08:00
using osu.Framework.Testing ;
2021-05-13 04:42:26 +08:00
using osu.Game.Extensions ;
2021-03-03 03:07:11 +08:00
using osu.Game.Rulesets.Filter ;
2021-08-22 22:40:17 +08:00
using osu.Game.Screens.Edit.Setup ;
2020-06-15 21:45:18 +08:00
using osu.Game.Screens.Ranking.Statistics ;
2018-04-13 17:19:50 +08:00
namespace osu.Game.Rulesets
{
2020-09-04 19:34:26 +08:00
[ExcludeFromDynamicCompile]
2018-04-13 17:19:50 +08:00
public abstract class Ruleset
{
2019-12-24 12:48:27 +08:00
public RulesetInfo RulesetInfo { get ; internal set ; }
2018-04-13 17:19:50 +08:00
public IEnumerable < Mod > GetAllMods ( ) = > Enum . GetValues ( typeof ( ModType ) ) . Cast < ModType > ( )
// Confine all mods of each mod type into a single IEnumerable<Mod>
. SelectMany ( GetModsFor )
// Filter out all null mods
. Where ( mod = > mod ! = null )
// Resolve MultiMods as their .Mods property
. SelectMany ( mod = > ( mod as MultiMod ) ? . Mods ? ? new [ ] { mod } ) ;
public abstract IEnumerable < Mod > GetModsFor ( ModType type ) ;
2018-04-16 20:14:40 +08:00
/// <summary>
/// Converts mods from legacy enum values. Do not override if you're not a legacy ruleset.
/// </summary>
2020-03-24 11:06:24 +08:00
/// <param name="mods">The legacy enum which will be converted.</param>
/// <returns>An enumerable of constructed <see cref="Mod"/>s.</returns>
public virtual IEnumerable < Mod > ConvertFromLegacyMods ( LegacyMods mods ) = > Array . Empty < Mod > ( ) ;
/// <summary>
/// Converts mods to legacy enum values. Do not override if you're not a legacy ruleset.
/// </summary>
/// <param name="mods">The mods which will be converted.</param>
/// <returns>A single bitwise enumerable value representing (to the best of our ability) the mods.</returns>
public virtual LegacyMods ConvertToLegacyMods ( Mod [ ] mods )
{
var value = LegacyMods . None ;
foreach ( var mod in mods )
{
switch ( mod )
{
case ModNoFail _ :
value | = LegacyMods . NoFail ;
break ;
case ModEasy _ :
value | = LegacyMods . Easy ;
break ;
case ModHidden _ :
value | = LegacyMods . Hidden ;
break ;
case ModHardRock _ :
value | = LegacyMods . HardRock ;
break ;
2020-11-15 22:35:06 +08:00
case ModPerfect _ :
value | = LegacyMods . Perfect ;
break ;
2020-03-24 11:06:24 +08:00
case ModSuddenDeath _ :
value | = LegacyMods . SuddenDeath ;
break ;
2020-11-15 22:35:06 +08:00
case ModNightcore _ :
value | = LegacyMods . Nightcore ;
break ;
2020-03-24 11:06:24 +08:00
case ModDoubleTime _ :
value | = LegacyMods . DoubleTime ;
break ;
case ModRelax _ :
value | = LegacyMods . Relax ;
break ;
case ModHalfTime _ :
value | = LegacyMods . HalfTime ;
break ;
case ModFlashlight _ :
value | = LegacyMods . Flashlight ;
break ;
2020-11-15 22:35:06 +08:00
case ModCinema _ :
value | = LegacyMods . Cinema ;
break ;
case ModAutoplay _ :
value | = LegacyMods . Autoplay ;
break ;
2020-03-24 11:06:24 +08:00
}
}
return value ;
}
2018-04-13 21:41:35 +08:00
2020-06-02 19:32:52 +08:00
[CanBeNull]
2020-06-01 23:41:04 +08:00
public ModAutoplay GetAutoplayMod ( ) = > GetAllMods ( ) . OfType < ModAutoplay > ( ) . FirstOrDefault ( ) ;
2018-04-13 17:19:50 +08:00
2021-06-10 18:41:41 +08:00
public virtual ISkin CreateLegacySkinProvider ( [ NotNull ] ISkin skin , IBeatmap beatmap ) = > null ;
2019-08-26 11:21:49 +08:00
2019-12-18 13:49:09 +08:00
protected Ruleset ( )
2018-04-13 17:19:50 +08:00
{
2019-12-24 15:02:35 +08:00
RulesetInfo = new RulesetInfo
{
Name = Description ,
ShortName = ShortName ,
ID = ( this as ILegacyRuleset ) ? . LegacyID ,
2021-05-13 04:42:26 +08:00
InstantiationInfo = GetType ( ) . GetInvariantInstantiationInfo ( ) ,
2020-01-03 19:39:15 +08:00
Available = true ,
2019-12-24 15:02:35 +08:00
} ;
2018-04-13 17:19:50 +08:00
}
/// <summary>
/// Attempt to create a hit renderer for a beatmap
/// </summary>
/// <param name="beatmap">The beatmap to create the hit renderer for.</param>
2019-04-25 16:36:17 +08:00
/// <param name="mods">The <see cref="Mod"/>s to apply.</param>
2018-04-13 17:19:50 +08:00
/// <exception cref="BeatmapInvalidForRulesetException">Unable to successfully load the beatmap to be usable with this ruleset.</exception>
2019-12-12 14:58:11 +08:00
public abstract DrawableRuleset CreateDrawableRulesetWith ( IBeatmap beatmap , IReadOnlyList < Mod > mods = null ) ;
2018-04-13 17:19:50 +08:00
2019-12-17 19:08:13 +08:00
/// <summary>
2019-12-24 16:01:17 +08:00
/// Creates a <see cref="ScoreProcessor"/> for this <see cref="Ruleset"/>.
2019-12-17 19:08:13 +08:00
/// </summary>
/// <returns>The score processor.</returns>
2019-12-24 16:01:17 +08:00
public virtual ScoreProcessor CreateScoreProcessor ( ) = > new ScoreProcessor ( ) ;
2019-12-17 19:08:13 +08:00
2019-12-19 19:03:14 +08:00
/// <summary>
2019-12-24 16:01:17 +08:00
/// Creates a <see cref="HealthProcessor"/> for this <see cref="Ruleset"/>.
2019-12-19 19:03:14 +08:00
/// </summary>
/// <returns>The health processor.</returns>
2019-12-27 15:14:49 +08:00
public virtual HealthProcessor CreateHealthProcessor ( double drainStartTime ) = > new DrainingHealthProcessor ( drainStartTime ) ;
2019-12-19 19:03:14 +08:00
2018-06-29 12:07:00 +08:00
/// <summary>
/// Creates a <see cref="IBeatmapConverter"/> to convert a <see cref="IBeatmap"/> to one that is applicable for this <see cref="Ruleset"/>.
/// </summary>
/// <param name="beatmap">The <see cref="IBeatmap"/> to be converted.</param>
/// <returns>The <see cref="IBeatmapConverter"/>.</returns>
2018-04-19 21:04:12 +08:00
public abstract IBeatmapConverter CreateBeatmapConverter ( IBeatmap beatmap ) ;
2018-06-29 12:07:00 +08:00
/// <summary>
/// Optionally creates a <see cref="IBeatmapProcessor"/> to alter a <see cref="IBeatmap"/> after it has been converted.
/// </summary>
/// <param name="beatmap">The <see cref="IBeatmap"/> to be processed.</param>
/// <returns>The <see cref="IBeatmapProcessor"/>.</returns>
2018-04-19 21:04:12 +08:00
public virtual IBeatmapProcessor CreateBeatmapProcessor ( IBeatmap beatmap ) = > null ;
2019-02-21 12:12:37 +08:00
public abstract DifficultyCalculator CreateDifficultyCalculator ( WorkingBeatmap beatmap ) ;
2018-04-13 17:19:50 +08:00
2020-10-07 16:46:57 +08:00
/// <summary>
/// Optionally creates a <see cref="PerformanceCalculator"/> to generate performance data from the provided score.
/// </summary>
/// <param name="attributes">Difficulty attributes for the beatmap related to the provided score.</param>
/// <param name="score">The score to be processed.</param>
/// <returns>A performance calculator instance for the provided score.</returns>
[CanBeNull]
2020-10-03 22:51:22 +08:00
public virtual PerformanceCalculator CreatePerformanceCalculator ( DifficultyAttributes attributes , ScoreInfo score ) = > null ;
2020-10-07 16:46:57 +08:00
/// <summary>
/// Optionally creates a <see cref="PerformanceCalculator"/> to generate performance data from the provided score.
/// </summary>
/// <param name="beatmap">The beatmap to use as a source for generating <see cref="DifficultyAttributes"/>.</param>
/// <param name="score">The score to be processed.</param>
/// <returns>A performance calculator instance for the provided score.</returns>
[CanBeNull]
2020-10-03 22:51:22 +08:00
public PerformanceCalculator CreatePerformanceCalculator ( WorkingBeatmap beatmap , ScoreInfo score )
{
var difficultyCalculator = CreateDifficultyCalculator ( beatmap ) ;
var difficultyAttributes = difficultyCalculator . Calculate ( score . Mods ) ;
return CreatePerformanceCalculator ( difficultyAttributes , score ) ;
}
2018-04-13 17:19:50 +08:00
public virtual HitObjectComposer CreateHitObjectComposer ( ) = > null ;
2021-04-13 16:40:56 +08:00
public virtual IBeatmapVerifier CreateBeatmapVerifier ( ) = > null ;
2021-04-07 20:36:43 +08:00
2019-04-02 18:55:24 +08:00
public virtual Drawable CreateIcon ( ) = > new SpriteIcon { Icon = FontAwesome . Solid . QuestionCircle } ;
2018-04-13 17:19:50 +08:00
2019-12-28 21:13:18 +08:00
public virtual IResourceStore < byte [ ] > CreateResourceStore ( ) = > new NamespacedResourceStore < byte [ ] > ( new DllResourceStore ( GetType ( ) . Assembly ) , @"Resources" ) ;
2019-09-04 19:28:21 +08:00
2018-04-13 17:19:50 +08:00
public abstract string Description { get ; }
2018-06-11 12:28:50 +08:00
public virtual RulesetSettingsSubsection CreateSettings ( ) = > null ;
2018-04-13 17:19:50 +08:00
2018-06-11 12:17:08 +08:00
/// <summary>
/// Creates the <see cref="IRulesetConfigManager"/> for this <see cref="Ruleset"/>.
/// </summary>
/// <param name="settings">The <see cref="SettingsStore"/> to store the settings.</param>
public virtual IRulesetConfigManager CreateConfig ( SettingsStore settings ) = > null ;
2018-04-13 17:19:50 +08:00
/// <summary>
/// A unique short name to reference this ruleset in online requests.
/// </summary>
public abstract string ShortName { get ; }
2020-01-03 19:39:15 +08:00
/// <summary>
2021-08-18 08:13:53 +08:00
/// The playing verb to be shown in the <see cref="UserActivity.InGame"/> activities.
2020-01-03 19:39:15 +08:00
/// </summary>
2021-08-14 22:39:12 +08:00
public virtual string PlayingVerb = > "Playing" ;
2020-01-03 19:39:15 +08:00
2018-04-13 17:19:50 +08:00
/// <summary>
/// A list of available variant ids.
/// </summary>
public virtual IEnumerable < int > AvailableVariants = > new [ ] { 0 } ;
/// <summary>
/// Get a list of default keys for the specified variant.
/// </summary>
/// <param name="variant">A variant.</param>
/// <returns>A list of valid <see cref="KeyBinding"/>s.</returns>
2019-11-28 21:41:29 +08:00
public virtual IEnumerable < KeyBinding > GetDefaultKeyBindings ( int variant = 0 ) = > Array . Empty < KeyBinding > ( ) ;
2018-04-13 17:19:50 +08:00
/// <summary>
/// Gets the name for a key binding variant. This is used for display in the settings overlay.
/// </summary>
/// <param name="variant">The variant.</param>
/// <returns>A descriptive name of the variant.</returns>
public virtual string GetVariantName ( int variant ) = > string . Empty ;
/// <summary>
/// For rulesets which support legacy (osu-stable) replay conversion, this method will create an empty replay frame
/// for conversion use.
/// </summary>
/// <returns>An empty frame for the current ruleset, or null if unsupported.</returns>
public virtual IConvertibleReplayFrame CreateConvertibleReplayFrame ( ) = > null ;
2020-06-15 21:45:18 +08:00
2020-06-19 19:53:43 +08:00
/// <summary>
/// Creates the statistics for a <see cref="ScoreInfo"/> to be displayed in the results screen.
/// </summary>
/// <param name="score">The <see cref="ScoreInfo"/> to create the statistics for. The score is guaranteed to have <see cref="ScoreInfo.HitEvents"/> populated.</param>
2020-06-22 17:38:41 +08:00
/// <param name="playableBeatmap">The <see cref="IBeatmap"/>, converted for this <see cref="Ruleset"/> with all relevant <see cref="Mod"/>s applied.</param>
2020-06-19 19:53:43 +08:00
/// <returns>The <see cref="StatisticRow"/>s to display. Each <see cref="StatisticRow"/> may contain 0 or more <see cref="StatisticItem"/>.</returns>
2020-06-19 19:31:52 +08:00
[NotNull]
2020-06-22 17:38:41 +08:00
public virtual StatisticRow [ ] CreateStatisticsForScore ( ScoreInfo score , IBeatmap playableBeatmap ) = > Array . Empty < StatisticRow > ( ) ;
2020-10-07 14:34:23 +08:00
/// <summary>
/// Get all valid <see cref="HitResult"/>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.
/// </summary>
/// <returns>
/// All valid <see cref="HitResult"/>s along with a display-friendly name.
/// </returns>
public IEnumerable < ( HitResult result , string displayName ) > GetHitResults ( )
{
var validResults = GetValidHitResults ( ) ;
// enumerate over ordered list to guarantee return order is stable.
2021-01-28 05:01:56 +08:00
foreach ( var result in EnumExtensions . GetValuesInOrder < HitResult > ( ) )
2020-10-07 14:34:23 +08:00
{
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 ) ) ;
}
}
/// <summary>
/// Get all valid <see cref="HitResult"/>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.
/// </summary>
/// <remarks>
/// <see cref="HitResult.Miss"/> is implicitly included. Special types like <see cref="HitResult.IgnoreHit"/> are ignored even when specified.
/// </remarks>
2021-01-28 05:01:56 +08:00
protected virtual IEnumerable < HitResult > GetValidHitResults ( ) = > EnumExtensions . GetValuesInOrder < HitResult > ( ) ;
2020-10-07 14:34:23 +08:00
/// <summary>
/// Get a display friendly name for the specified result type.
/// </summary>
/// <param name="result">The result type to get the name for.</param>
/// <returns>The display name.</returns>
public virtual string GetDisplayNameForHitResult ( HitResult result ) = > result . GetDescription ( ) ;
2021-03-03 03:07:11 +08:00
/// <summary>
/// Creates ruleset-specific beatmap filter criteria to be used on the song select screen.
/// </summary>
[CanBeNull]
public virtual IRulesetFilterCriteria CreateRulesetFilterCriteria ( ) = > null ;
2021-08-22 22:40:17 +08:00
/// <summary>
/// Can be overridden to add a ruleset-specific section to the editor beatmap setup screen.
/// </summary>
[CanBeNull]
public virtual RulesetSetupSection CreateEditorSetupSectionForRuleset ( ) = > null ;
2018-04-13 17:19:50 +08:00
}
}