2019-02-12 07:01:25 +00:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2019-01-24 08:43:03 +00:00
// See the LICENCE file in the repository root for full licence text.
2018-04-13 09:19:50 +00:00
2018-06-06 07:20:17 +00:00
using System ;
2018-04-13 09:19:50 +00:00
using System.Collections.Generic ;
using System.Linq ;
2018-05-15 08:38:04 +00:00
using osu.Game.Beatmaps ;
2019-02-12 07:01:25 +00:00
using osu.Game.Rulesets.Difficulty.Preprocessing ;
using osu.Game.Rulesets.Difficulty.Skills ;
2018-05-15 08:38:04 +00:00
using osu.Game.Rulesets.Mods ;
2018-04-13 09:19:50 +00:00
2018-05-15 08:38:04 +00:00
namespace osu.Game.Rulesets.Difficulty
2018-04-13 09:19:50 +00:00
{
2019-02-12 07:01:25 +00:00
public abstract class DifficultyCalculator : LegacyDifficultyCalculator
2018-04-13 09:19:50 +00:00
{
2019-02-12 07:01:25 +00:00
/// <summary>
/// The length of each strain section.
/// </summary>
protected virtual int SectionLength = > 400 ;
2018-04-13 09:19:50 +00:00
2018-06-14 06:32:07 +00:00
protected DifficultyCalculator ( Ruleset ruleset , WorkingBeatmap beatmap )
2019-02-12 07:01:25 +00:00
: base ( ruleset , beatmap )
2018-04-13 09:19:50 +00:00
{
}
2019-02-19 05:29:23 +00:00
protected override DifficultyAttributes Calculate ( IBeatmap beatmap , Mod [ ] mods , double clockRate )
2018-04-13 09:19:50 +00:00
{
2019-02-19 04:40:39 +00:00
var attributes = CreateDifficultyAttributes ( ) ;
attributes . Mods = mods ;
2018-06-14 06:32:07 +00:00
2019-02-12 07:01:25 +00:00
if ( ! beatmap . HitObjects . Any ( ) )
return attributes ;
2018-06-14 06:32:07 +00:00
2019-02-19 05:29:23 +00:00
var difficultyHitObjects = CreateDifficultyHitObjects ( beatmap , clockRate ) . OrderBy ( h = > h . BaseObject . StartTime ) . ToList ( ) ;
2019-02-12 07:01:25 +00:00
var skills = CreateSkills ( ) ;
2018-06-14 06:32:07 +00:00
2019-02-19 05:29:23 +00:00
double sectionLength = SectionLength * clockRate ;
2019-02-12 07:01:25 +00:00
// The first object doesn't generate a strain, so we begin with an incremented section end
double currentSectionEnd = Math . Ceiling ( beatmap . HitObjects . First ( ) . StartTime / sectionLength ) * sectionLength ;
foreach ( DifficultyHitObject h in difficultyHitObjects )
2018-06-14 06:32:07 +00:00
{
2019-02-12 07:01:25 +00:00
while ( h . BaseObject . StartTime > currentSectionEnd )
{
foreach ( Skill s in skills )
{
s . SaveCurrentPeak ( ) ;
s . StartNewSectionFrom ( currentSectionEnd ) ;
}
currentSectionEnd + = sectionLength ;
}
foreach ( Skill s in skills )
s . Process ( h ) ;
2018-06-14 06:32:07 +00:00
}
2019-02-12 07:01:25 +00:00
// The peak strain will not be saved for the last section in the above loop
foreach ( Skill s in skills )
s . SaveCurrentPeak ( ) ;
2019-02-19 05:29:23 +00:00
PopulateAttributes ( attributes , beatmap , skills , clockRate ) ;
2019-02-12 07:01:25 +00:00
return attributes ;
2018-04-13 09:19:50 +00:00
}
2018-06-06 07:20:17 +00:00
/// <summary>
/// Creates all <see cref="Mod"/> combinations which adjust the <see cref="Beatmap"/> difficulty.
/// </summary>
public Mod [ ] CreateDifficultyAdjustmentModCombinations ( )
{
return createDifficultyAdjustmentModCombinations ( Enumerable . Empty < Mod > ( ) , DifficultyAdjustmentMods ) . ToArray ( ) ;
IEnumerable < Mod > createDifficultyAdjustmentModCombinations ( IEnumerable < Mod > currentSet , Mod [ ] adjustmentSet , int currentSetCount = 0 , int adjustmentSetStart = 0 )
{
2018-07-17 05:35:09 +00:00
switch ( currentSetCount )
{
case 0 :
// Initial-case: Empty current set
2018-11-30 08:35:13 +00:00
yield return new ModNoMod ( ) ;
2018-07-17 05:35:09 +00:00
break ;
case 1 :
yield return currentSet . Single ( ) ;
break ;
2018-07-17 07:33:08 +00:00
default :
yield return new MultiMod ( currentSet . ToArray ( ) ) ;
break ;
2018-07-17 05:35:09 +00:00
}
2018-06-06 07:20:17 +00:00
// Apply mods in the adjustment set recursively. Using the entire adjustment set would result in duplicate multi-mod mod
// combinations in further recursions, so a moving subset is used to eliminate this effect
for ( int i = adjustmentSetStart ; i < adjustmentSet . Length ; i + + )
{
var adjustmentMod = adjustmentSet [ i ] ;
if ( currentSet . Any ( c = > c . IncompatibleMods . Any ( m = > m . IsInstanceOfType ( adjustmentMod ) ) ) )
continue ;
foreach ( var combo in createDifficultyAdjustmentModCombinations ( currentSet . Append ( adjustmentMod ) , adjustmentSet , currentSetCount + 1 , i + 1 ) )
yield return combo ;
}
}
}
/// <summary>
/// Retrieves all <see cref="Mod"/>s which adjust the <see cref="Beatmap"/> difficulty.
/// </summary>
protected virtual Mod [ ] DifficultyAdjustmentMods = > Array . Empty < Mod > ( ) ;
2018-06-14 06:32:07 +00:00
/// <summary>
2019-02-12 07:01:25 +00:00
/// Populates <see cref="DifficultyAttributes"/> after difficulty has been processed.
/// </summary>
/// <param name="attributes">The <see cref="DifficultyAttributes"/> to populate with information about the difficulty of <paramref name="beatmap"/>.</param>
/// <param name="beatmap">The <see cref="IBeatmap"/> whose difficulty was processed.</param>
/// <param name="skills">The skills which processed the difficulty.</param>
2019-02-19 05:29:23 +00:00
/// <param name="clockRate">The rate at which the gameplay clock is run at.</param>
protected abstract void PopulateAttributes ( DifficultyAttributes attributes , IBeatmap beatmap , Skill [ ] skills , double clockRate ) ;
2019-02-12 07:01:25 +00:00
/// <summary>
/// Enumerates <see cref="DifficultyHitObject"/>s to be processed from <see cref="HitObject"/>s in the <see cref="IBeatmap"/>.
2018-06-14 06:32:07 +00:00
/// </summary>
2019-02-12 07:01:25 +00:00
/// <param name="beatmap">The <see cref="IBeatmap"/> providing the <see cref="HitObject"/>s to enumerate.</param>
2019-02-19 05:29:23 +00:00
/// <param name="clockRate">The rate at which the gameplay clock is run at.</param>
2019-02-12 07:01:25 +00:00
/// <returns>The enumerated <see cref="DifficultyHitObject"/>s.</returns>
2019-02-19 05:29:23 +00:00
protected abstract IEnumerable < DifficultyHitObject > CreateDifficultyHitObjects ( IBeatmap beatmap , double clockRate ) ;
2019-02-12 07:01:25 +00:00
/// <summary>
/// Creates the <see cref="Skill"/>s to calculate the difficulty of <see cref="DifficultyHitObject"/>s.
/// </summary>
/// <returns>The <see cref="Skill"/>s.</returns>
protected abstract Skill [ ] CreateSkills ( ) ;
/// <summary>
/// Creates an empty <see cref="DifficultyAttributes"/>.
/// </summary>
/// <returns>The empty <see cref="DifficultyAttributes"/>.</returns>
2019-02-19 04:40:39 +00:00
protected abstract DifficultyAttributes CreateDifficultyAttributes ( ) ;
2018-04-13 09:19:50 +00:00
}
}