mirror of
https://github.com/ppy/osu.git
synced 2024-11-07 23:13:34 +08:00
Merge pull request #11689 from Syriiin/diffcalc/refactor-strain-logic
Refactor to encapsulate strain logic into Skill class
This commit is contained in:
commit
f51bd10e2d
@ -21,8 +21,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
{
|
||||
private const double star_scaling_factor = 0.153;
|
||||
|
||||
protected override int SectionLength => 750;
|
||||
|
||||
private float halfCatcherWidth;
|
||||
|
||||
public CatchDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||
|
@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||
|
||||
protected override double DecayWeight => 0.94;
|
||||
|
||||
protected override int SectionLength => 750;
|
||||
|
||||
protected readonly float HalfCatcherWidth;
|
||||
|
||||
private float? lastPlayerPosition;
|
||||
|
@ -7,7 +7,6 @@ using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||
{
|
||||
@ -36,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||
protected override double StrainValueOf(DifficultyHitObject current)
|
||||
{
|
||||
var maniaCurrent = (ManiaDifficultyHitObject)current;
|
||||
var endTime = maniaCurrent.BaseObject.GetEndTime();
|
||||
var endTime = maniaCurrent.EndTime;
|
||||
var column = maniaCurrent.BaseObject.Column;
|
||||
|
||||
double holdFactor = 1.0; // Factor to all additional strains in case something else is held
|
||||
@ -46,7 +45,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||
for (int i = 0; i < holdEndTimes.Length; ++i)
|
||||
{
|
||||
// If there is at least one other overlapping end or note, then we get an addition, buuuuuut...
|
||||
if (Precision.DefinitelyBigger(holdEndTimes[i], maniaCurrent.BaseObject.StartTime, 1) && Precision.DefinitelyBigger(endTime, holdEndTimes[i], 1))
|
||||
if (Precision.DefinitelyBigger(holdEndTimes[i], maniaCurrent.StartTime, 1) && Precision.DefinitelyBigger(endTime, holdEndTimes[i], 1))
|
||||
holdAddition = 1.0;
|
||||
|
||||
// ... this addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1
|
||||
|
@ -133,11 +133,16 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
{
|
||||
List<double> peaks = new List<double>();
|
||||
|
||||
for (int i = 0; i < colour.StrainPeaks.Count; i++)
|
||||
var colourPeaks = colour.GetCurrentStrainPeaks().ToList();
|
||||
var rhythmPeaks = rhythm.GetCurrentStrainPeaks().ToList();
|
||||
var staminaRightPeaks = staminaRight.GetCurrentStrainPeaks().ToList();
|
||||
var staminaLeftPeaks = staminaLeft.GetCurrentStrainPeaks().ToList();
|
||||
|
||||
for (int i = 0; i < colourPeaks.Count; i++)
|
||||
{
|
||||
double colourPeak = colour.StrainPeaks[i] * colour_skill_multiplier;
|
||||
double rhythmPeak = rhythm.StrainPeaks[i] * rhythm_skill_multiplier;
|
||||
double staminaPeak = (staminaRight.StrainPeaks[i] + staminaLeft.StrainPeaks[i]) * stamina_skill_multiplier * staminaPenalty;
|
||||
double colourPeak = colourPeaks[i] * colour_skill_multiplier;
|
||||
double rhythmPeak = rhythmPeaks[i] * rhythm_skill_multiplier;
|
||||
double staminaPeak = (staminaRightPeaks[i] + staminaLeftPeaks[i]) * stamina_skill_multiplier * staminaPenalty;
|
||||
peaks.Add(norm(2, colourPeak, rhythmPeak, staminaPeak));
|
||||
}
|
||||
|
||||
|
@ -16,11 +16,6 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
{
|
||||
public abstract class DifficultyCalculator
|
||||
{
|
||||
/// <summary>
|
||||
/// The length of each strain section.
|
||||
/// </summary>
|
||||
protected virtual int SectionLength => 400;
|
||||
|
||||
private readonly Ruleset ruleset;
|
||||
private readonly WorkingBeatmap beatmap;
|
||||
|
||||
@ -71,32 +66,14 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
|
||||
var difficultyHitObjects = SortObjects(CreateDifficultyHitObjects(beatmap, clockRate)).ToList();
|
||||
|
||||
double sectionLength = SectionLength * clockRate;
|
||||
|
||||
// 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)
|
||||
foreach (var hitObject in difficultyHitObjects)
|
||||
{
|
||||
while (h.BaseObject.StartTime > currentSectionEnd)
|
||||
foreach (var skill in skills)
|
||||
{
|
||||
foreach (Skill s in skills)
|
||||
{
|
||||
s.SaveCurrentPeak();
|
||||
s.StartNewSectionFrom(currentSectionEnd / clockRate);
|
||||
skill.Process(hitObject);
|
||||
}
|
||||
|
||||
currentSectionEnd += sectionLength;
|
||||
}
|
||||
|
||||
foreach (Skill s in skills)
|
||||
s.Process(h);
|
||||
}
|
||||
|
||||
// The peak strain will not be saved for the last section in the above loop
|
||||
foreach (Skill s in skills)
|
||||
s.SaveCurrentPeak();
|
||||
|
||||
return CreateDifficultyAttributes(beatmap, mods, skills, clockRate);
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Difficulty.Preprocessing
|
||||
public readonly HitObject LastObject;
|
||||
|
||||
/// <summary>
|
||||
/// Amount of time elapsed between <see cref="BaseObject"/> and <see cref="LastObject"/>.
|
||||
/// Amount of time elapsed between <see cref="BaseObject"/> and <see cref="LastObject"/>, adjusted by clockrate.
|
||||
/// </summary>
|
||||
public readonly double DeltaTime;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// 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;
|
||||
@ -16,11 +16,6 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
/// </summary>
|
||||
public abstract class Skill
|
||||
{
|
||||
/// <summary>
|
||||
/// The peak strain for each <see cref="DifficultyCalculator.SectionLength"/> section of the beatmap.
|
||||
/// </summary>
|
||||
public IReadOnlyList<double> StrainPeaks => strainPeaks;
|
||||
|
||||
/// <summary>
|
||||
/// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other.
|
||||
/// </summary>
|
||||
@ -47,6 +42,11 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
/// </summary>
|
||||
protected double CurrentStrain { get; private set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The length of each strain section.
|
||||
/// </summary>
|
||||
protected virtual int SectionLength => 400;
|
||||
|
||||
/// <summary>
|
||||
/// Mods for use in skill calculations.
|
||||
/// </summary>
|
||||
@ -54,6 +54,8 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
|
||||
private double currentSectionPeak = 1; // We also keep track of the peak strain level in the current section.
|
||||
|
||||
private double currentSectionEnd;
|
||||
|
||||
private readonly List<double> strainPeaks = new List<double>();
|
||||
|
||||
private readonly Mod[] mods;
|
||||
@ -68,6 +70,17 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
/// </summary>
|
||||
public void Process(DifficultyHitObject current)
|
||||
{
|
||||
// The first object doesn't generate a strain, so we begin with an incremented section end
|
||||
if (Previous.Count == 0)
|
||||
currentSectionEnd = Math.Ceiling(current.StartTime / SectionLength) * SectionLength;
|
||||
|
||||
while (current.StartTime > currentSectionEnd)
|
||||
{
|
||||
saveCurrentPeak();
|
||||
startNewSectionFrom(currentSectionEnd);
|
||||
currentSectionEnd += SectionLength;
|
||||
}
|
||||
|
||||
CurrentStrain *= strainDecay(current.DeltaTime);
|
||||
CurrentStrain += StrainValueOf(current) * SkillMultiplier;
|
||||
|
||||
@ -79,21 +92,19 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
/// <summary>
|
||||
/// Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty.
|
||||
/// </summary>
|
||||
public void SaveCurrentPeak()
|
||||
private void saveCurrentPeak()
|
||||
{
|
||||
if (Previous.Count > 0)
|
||||
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, adjusted by clockrate.</param>
|
||||
public void StartNewSectionFrom(double time)
|
||||
/// <param name="time">The beginning of the new section in milliseconds.</param>
|
||||
private void startNewSectionFrom(double time)
|
||||
{
|
||||
// The maximum strain of the new section is not zero by default, strain decays as usual regardless of section boundaries.
|
||||
// This means we need to capture the strain level at the beginning of the new section, and use that as the initial peak level.
|
||||
if (Previous.Count > 0)
|
||||
currentSectionPeak = GetPeakStrain(time);
|
||||
}
|
||||
|
||||
@ -105,7 +116,13 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
protected virtual double GetPeakStrain(double time) => CurrentStrain * strainDecay(time - Previous[0].StartTime);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the calculated difficulty value representing all processed <see cref="DifficultyHitObject"/>s.
|
||||
/// Returns a live enumerable of the peak strains for each <see cref="SectionLength"/> section of the beatmap,
|
||||
/// including the peak of the current section.
|
||||
/// </summary>
|
||||
public IEnumerable<double> GetCurrentStrainPeaks() => strainPeaks.Append(currentSectionPeak);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the calculated difficulty value representing all <see cref="DifficultyHitObject"/>s that have been processed up to this point.
|
||||
/// </summary>
|
||||
public double DifficultyValue()
|
||||
{
|
||||
@ -114,7 +131,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
|
||||
// Difficulty is the weighted sum of the highest strains from every section.
|
||||
// We're sorting from highest to lowest strain.
|
||||
foreach (double strain in strainPeaks.OrderByDescending(d => d))
|
||||
foreach (double strain in GetCurrentStrainPeaks().OrderByDescending(d => d))
|
||||
{
|
||||
difficulty += strain * weight;
|
||||
weight *= DecayWeight;
|
||||
|
Loading…
Reference in New Issue
Block a user