1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-24 16:42:58 +08:00
osu-lazer/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

103 lines
4.2 KiB
C#
Raw Normal View History

// 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.
using System;
using System.Linq;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Difficulty.Skills
{
/// <summary>
/// Used to processes strain values of <see cref="DifficultyHitObject"/>s, keep track of strain levels caused by the processed objects
/// and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
/// </summary>
public abstract class StrainSkill : GraphSkill
{
/// <summary>
/// The weight by which each strain value decays.
/// </summary>
protected virtual double DecayWeight => 0.9;
protected StrainSkill(Mod[] mods)
: base(mods)
{
}
/// <summary>
2021-08-17 06:36:14 +08:00
/// Returns the strain value at <see cref="DifficultyHitObject"/>. This value is calculated with or without respect to previous objects.
/// </summary>
protected abstract double StrainValueAt(DifficultyHitObject current);
/// <summary>
/// Process a <see cref="DifficultyHitObject"/> and update current strain values accordingly.
/// </summary>
2022-05-23 04:45:27 +08:00
public sealed override void Process(DifficultyHitObject current)
{
// The first object doesn't generate a strain, so we begin with an incremented section end
2022-06-09 17:49:11 +08:00
if (current.Index == 0)
CurrentSectionEnd = Math.Ceiling(current.StartTime / SectionLength) * SectionLength;
while (current.StartTime > CurrentSectionEnd)
{
saveCurrentPeak();
startNewSectionFrom(CurrentSectionEnd, current);
CurrentSectionEnd += SectionLength;
}
CurrentSectionPeak = Math.Max(StrainValueAt(current), CurrentSectionPeak);
}
/// <summary>
/// Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty.
/// </summary>
private void saveCurrentPeak()
{
StrainPeaks.Add(CurrentSectionPeak);
}
/// <summary>
/// Sets the initial strain level for a new section.
/// </summary>
/// <param name="time">The beginning of the new section in milliseconds.</param>
/// <param name="current">The current hit object.</param>
private void startNewSectionFrom(double time, DifficultyHitObject current)
{
// The maximum strain of the new section is not zero by default
// This means we need to capture the strain level at the beginning of the new section, and use that as the initial peak level.
CurrentSectionPeak = CalculateInitialStrain(time, current);
}
/// <summary>
/// Retrieves the peak strain at a point in time.
/// </summary>
/// <param name="time">The time to retrieve the peak strain at.</param>
/// <param name="current">The current hit object.</param>
/// <returns>The peak strain.</returns>
protected abstract double CalculateInitialStrain(double time, DifficultyHitObject current);
/// <summary>
/// Returns the calculated difficulty value representing all <see cref="DifficultyHitObject"/>s that have been processed up to this point.
/// </summary>
2021-06-16 09:34:46 +08:00
public override double DifficultyValue()
{
double difficulty = 0;
double weight = 1;
// Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871).
// These sections will not contribute to the difficulty.
var peaks = GetCurrentStrainPeaks().Where(p => p > 0);
// Difficulty is the weighted sum of the highest strains from every section.
// We're sorting from highest to lowest strain.
foreach (double strain in peaks.OrderDescending())
{
difficulty += strain * weight;
weight *= DecayWeight;
}
return difficulty;
}
}
}