1
0
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:
Dan Balasescu 2021-04-05 15:09:34 +09:00 committed by GitHub
commit f51bd10e2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 49 additions and 51 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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;