2018-04-13 17:19:50 +08:00
|
|
|
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
|
|
|
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
|
|
|
|
2018-06-06 15:20:17 +08:00
|
|
|
|
using System;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
2018-05-15 16:38:04 +08:00
|
|
|
|
using osu.Framework.Timing;
|
|
|
|
|
using osu.Game.Beatmaps;
|
|
|
|
|
using osu.Game.Rulesets.Mods;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-05-15 16:38:04 +08:00
|
|
|
|
namespace osu.Game.Rulesets.Difficulty
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
|
|
|
|
public abstract class DifficultyCalculator
|
|
|
|
|
{
|
2018-06-14 14:32:07 +08:00
|
|
|
|
private readonly Ruleset ruleset;
|
|
|
|
|
private readonly WorkingBeatmap beatmap;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-06-14 14:32:07 +08:00
|
|
|
|
protected DifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
2018-06-14 14:32:07 +08:00
|
|
|
|
this.ruleset = ruleset;
|
|
|
|
|
this.beatmap = beatmap;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-14 14:32:07 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Calculates the difficulty of the beatmap using a specific mod combination.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="mods">The mods that should be applied to the beatmap.</param>
|
|
|
|
|
/// <returns>A structure describing the difficulty of the beatmap.</returns>
|
|
|
|
|
public DifficultyAttributes Calculate(params Mod[] mods)
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
2018-06-14 14:32:07 +08:00
|
|
|
|
beatmap.Mods.Value = mods;
|
|
|
|
|
IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo);
|
|
|
|
|
|
2018-04-13 17:19:50 +08:00
|
|
|
|
var clock = new StopwatchClock();
|
|
|
|
|
mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock));
|
2018-06-14 14:32:07 +08:00
|
|
|
|
|
|
|
|
|
return Calculate(playableBeatmap, mods, clock.Rate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Calculates the difficulty of the beatmap using all mod combinations applicable to the beatmap.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>A collection of structures describing the difficulty of the beatmap for each mod combination.</returns>
|
|
|
|
|
public IEnumerable<DifficultyAttributes> CalculateAll()
|
|
|
|
|
{
|
|
|
|
|
foreach (var combination in CreateDifficultyAdjustmentModCombinations())
|
|
|
|
|
{
|
|
|
|
|
if (combination is MultiMod multi)
|
|
|
|
|
yield return Calculate(multi.Mods);
|
|
|
|
|
else
|
|
|
|
|
yield return Calculate(combination);
|
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-06 15:20:17 +08: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 13:35:09 +08:00
|
|
|
|
switch (currentSetCount)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
// Initial-case: Empty current set
|
|
|
|
|
yield return new NoModMod();
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
yield return currentSet.Single();
|
|
|
|
|
break;
|
2018-07-17 15:33:08 +08:00
|
|
|
|
default:
|
|
|
|
|
yield return new MultiMod(currentSet.ToArray());
|
|
|
|
|
break;
|
2018-07-17 13:35:09 +08:00
|
|
|
|
}
|
2018-06-06 15:20:17 +08: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 14:32:07 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Calculates the difficulty of a <see cref="Beatmap"/> using a specific <see cref="Mod"/> combination.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="beatmap">The <see cref="IBeatmap"/> to compute the difficulty for.</param>
|
|
|
|
|
/// <param name="mods">The <see cref="Mod"/>s that should be applied.</param>
|
|
|
|
|
/// <param name="timeRate">The rate of time in <paramref name="beatmap"/>.</param>
|
|
|
|
|
/// <returns>A structure containing the difficulty attributes.</returns>
|
|
|
|
|
protected abstract DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
}
|
|
|
|
|
}
|