Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

January 2025 Diffcalc/PP release #31595

Open
wants to merge 39 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
79a3afe
Implement considerations for Relax within osu!taiko diffcalc (#30591)
Lawtrohux Dec 18, 2024
0f2f25d
Adjust `DifficultyValue` curve to avoid lower star rating of osu!taik…
YaniFR Dec 18, 2024
4ca88ae
Refactor `TaikoDifficultyCalculator` and add `DifficultStrain` attrib…
Lawtrohux Dec 19, 2024
ecd6b41
Increase `accscalingshift` and include `countok` in hit proportion (#…
Lawtrohux Dec 19, 2024
d8c3d89
remove particular condition on convert nerf (#31196)
Lawtrohux Dec 19, 2024
f722f94
Simplify osu! high-bpm acute angle jumps bonus (#30902)
stanriders Dec 20, 2024
f6a36f7
Implement `Reading` Skill into osu!taiko (#31208)
Lawtrohux Dec 21, 2024
6808a5a
Change slider drop penalty to use actual number of difficult sliders,…
stanriders Dec 21, 2024
3ddeaf8
Use `lastAngle` when nerfing repeated angles on acute bonus (#31245)
tsunyoku Dec 24, 2024
824497d
Rewrite of the `Rhythm` Skill within osu!taiko (#31284)
Lawtrohux Dec 27, 2024
988ed37
Add basic difficulty & performance calculation for Autopilot mod on o…
tsunyoku Dec 29, 2024
76ac11f
Fix angle bonuses calculating repetition incorrectly, apply distance …
stanriders Jan 6, 2025
4095b26
Add `consistentRatioPenalty` to the `Colour` skill. (#31285)
Lawtrohux Jan 7, 2025
3b58d5e
Clamp OD in performance calculation to fix negative OD gaining pp (#3…
stanriders Jan 7, 2025
392bb57
Simplify angle bonus formula (#31449)
stanriders Jan 8, 2025
db58ec8
Apply a bunch of balancing changes to aim (#31456)
stanriders Jan 9, 2025
b21c645
Punish speed PP for scores with high deviation (#30907)
Givikap120 Jan 9, 2025
c53188c
Use total deviation to scale accuracy on aim, general aim buff (#31498)
stanriders Jan 14, 2025
6cf15e3
Remove problematic total deviation scaling, rebalance aim (#31515)
tsunyoku Jan 14, 2025
5bed7c2
Remove lower cap on deviation without misses (#31499)
Natelytle Jan 14, 2025
0a21183
reading mono nerf (#31510)
Lawtrohux Jan 15, 2025
974fa76
fix spinners not increasing cumulative strain time (#31525)
molneya Jan 16, 2025
9da8dcd
osu!taiko stamina balancing (#31337)
Lawtrohux Jan 16, 2025
b9894f6
Bump NVika tool to 4.0.0
bdach Jan 16, 2025
a83f917
osu!taiko star rating and performance points rebalance (#31338)
Lawtrohux Jan 16, 2025
a42c03c
osu!taiko further considerations for rhythm (#31339)
Lawtrohux Jan 17, 2025
5b4ba92
Move error function from osu.Game.Utils to osu.Game.Rulesets.Difficul…
Natelytle Jan 17, 2025
8354cd5
Penalise the reading difficulty of high velocity notes using "note de…
buyaspacecube Jan 18, 2025
67723b3
Fix osu!catch "buzz slider" SR abuse (#31126)
bastoo0 Jan 18, 2025
e320f17
Remove redundant angle check (#31566)
tsunyoku Jan 19, 2025
e04727a
Improve convert considerations in osu!taiko (#31546)
Lawtrohux Jan 19, 2025
2d0bc6c
Rebalance stamina length bonus in osu!taiko (#31556)
Lawtrohux Jan 19, 2025
e575654
osu!taiko new rhythm penalty for long intervals using stamina difficu…
buyaspacecube Jan 20, 2025
22e839d
Replace indexed skill access with `skills.OfType<...>().Single()` (#3…
stanriders Jan 20, 2025
a77dfb1
Use correct `HitWindows` class for osu!taiko hit windows in difficult…
tsunyoku Jan 20, 2025
aeca37c
Merge branch 'master' into pp-dev
peppy Jan 21, 2025
c8b05ce
Tidy up code quality of `RhythmEvaluator`
peppy Jan 21, 2025
4614496
Remove unnecessary strain sorting in difficult slider count (#31724)
Rian8337 Jan 29, 2025
2ee480c
Clamp `estimateImproperlyFollowedDifficultSliders` between 0 and `att…
tsunyoku Jan 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Difficulty.Preprocessing;
Expand Down Expand Up @@ -40,7 +41,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat

CatchDifficultyAttributes attributes = new CatchDifficultyAttributes
{
StarRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier,
StarRating = Math.Sqrt(skills.OfType<Movement>().Single().DifficultyValue()) * difficulty_multiplier,
Mods = mods,
ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
MaxCombo = beatmap.GetMaxCombo(),
Expand Down
25 changes: 24 additions & 1 deletion osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ public class Movement : StrainDecaySkill

private float? lastPlayerPosition;
private float lastDistanceMoved;
private float lastExactDistanceMoved;
private double lastStrainTime;
private bool isBuzzSliderTriggered;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about isInBuzzSection?


/// <summary>
/// The speed multiplier applied to the player's catcher.
Expand Down Expand Up @@ -59,6 +61,9 @@ protected override double StrainValueOf(DifficultyHitObject current)

float distanceMoved = playerPosition - lastPlayerPosition.Value;

// For the exact position we consider that the catcher is in the correct position for both objects
float exactDistanceMoved = catchCurrent.NormalizedPosition - lastPlayerPosition.Value;

double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catcherSpeedMultiplier);

double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510);
Expand Down Expand Up @@ -92,12 +97,30 @@ protected override double StrainValueOf(DifficultyHitObject current)
playerPosition = catchCurrent.NormalizedPosition;
}

distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catcherSpeedMultiplier, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values
distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash) / 20)
* Math.Pow((Math.Min(catchCurrent.StrainTime * catcherSpeedMultiplier, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values
}

// There is an edge case where horizontal back and forth sliders create "buzz" patterns which are repeated "movements" with a distance lower than
// the platter's width but high enough to be considered a movement due to the absolute_player_positioning_error and normalized_hitobject_radius offsets
// We are detecting this exact scenario. The first back and forth is counted but all subsequent ones are nullified.
// To achieve that, we need to store the exact distances (distance ignoring absolute_player_positioning_error and normalized_hitobject_radius)
if (Math.Abs(exactDistanceMoved) <= HalfCatcherWidth * 2 && exactDistanceMoved == -lastExactDistanceMoved && catchCurrent.StrainTime == lastStrainTime)
{
if (isBuzzSliderTriggered)
distanceAddition = 0;
else
isBuzzSliderTriggered = true;
}
else
{
isBuzzSliderTriggered = false;
}

lastPlayerPosition = playerPosition;
lastDistanceMoved = distanceMoved;
lastStrainTime = catchCurrent.StrainTime;
lastExactDistanceMoved = exactDistanceMoved;

return distanceAddition / weightedStrainTime;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat

ManiaDifficultyAttributes attributes = new ManiaDifficultyAttributes
{
StarRating = skills[0].DifficultyValue() * difficulty_multiplier,
StarRating = skills.OfType<Strain>().Single().DifficultyValue() * difficulty_multiplier,
Mods = mods,
// In osu-stable mania, rate-adjustment mods don't affect the hit window.
// This is done the way it is to introduce fractional differences in order to match osu-stable for the time being.
Expand Down
18 changes: 9 additions & 9 deletions osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,22 @@ public class OsuDifficultyCalculatorTest : DifficultyCalculatorTest
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu.Tests";

[TestCase(6.7171144000821119d, 239, "diffcalc-test")]
[TestCase(1.4485749025771304d, 54, "zero-length-sliders")]
[TestCase(0.42630400627180914d, 4, "very-fast-slider")]
[TestCase(6.7331304290522747d, 239, "diffcalc-test")]
[TestCase(1.4602604078137214d, 54, "zero-length-sliders")]
[TestCase(0.43052813047866129d, 4, "very-fast-slider")]
[TestCase(0.14143808967817237d, 2, "nan-slider")]
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
=> base.Test(expectedStarRating, expectedMaxCombo, name);

[TestCase(8.9825709931204205d, 239, "diffcalc-test")]
[TestCase(1.7550169162648608d, 54, "zero-length-sliders")]
[TestCase(0.55231632896800109d, 4, "very-fast-slider")]
[TestCase(9.6779746353001634d, 239, "diffcalc-test")]
[TestCase(1.7691451263718989d, 54, "zero-length-sliders")]
[TestCase(0.55785578988249407d, 4, "very-fast-slider")]
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime());

[TestCase(6.7171144000821119d, 239, "diffcalc-test")]
[TestCase(1.4485749025771304d, 54, "zero-length-sliders")]
[TestCase(0.42630400627180914d, 4, "very-fast-slider")]
[TestCase(6.7331304290522747d, 239, "diffcalc-test")]
[TestCase(1.4602604078137214d, 54, "zero-length-sliders")]
[TestCase(0.43052813047866129d, 4, "very-fast-slider")]
public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name)
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic());

Expand Down
48 changes: 29 additions & 19 deletions osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
public static class AimEvaluator
{
private const double wide_angle_multiplier = 1.5;
private const double acute_angle_multiplier = 1.95;
private const double acute_angle_multiplier = 2.6;
private const double slider_multiplier = 1.35;
private const double velocity_change_multiplier = 0.75;
private const double wiggle_multiplier = 1.02;

/// <summary>
/// Evaluates the difficulty of aiming the current object, based on:
Expand Down Expand Up @@ -64,37 +65,44 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with
double acuteAngleBonus = 0;
double sliderBonus = 0;
double velocityChangeBonus = 0;
double wiggleBonus = 0;

double aimStrain = currVelocity; // Start strain with regular velocity.

if (Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime) < 1.25 * Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime)) // If rhythms are the same.
{
if (osuCurrObj.Angle != null && osuLastObj.Angle != null && osuLastLastObj.Angle != null)
if (osuCurrObj.Angle != null && osuLastObj.Angle != null)
{
double currAngle = osuCurrObj.Angle.Value;
double lastAngle = osuLastObj.Angle.Value;
double lastLastAngle = osuLastLastObj.Angle.Value;

// Rewarding angles, take the smaller velocity as base.
double angleBonus = Math.Min(currVelocity, prevVelocity);

wideAngleBonus = calcWideAngleBonus(currAngle);
acuteAngleBonus = calcAcuteAngleBonus(currAngle);

if (DifficultyCalculationUtils.MillisecondsToBPM(osuCurrObj.StrainTime, 2) < 300) // Only buff deltaTime exceeding 300 bpm 1/2.
acuteAngleBonus = 0;
else
{
acuteAngleBonus *= calcAcuteAngleBonus(lastAngle) // Multiply by previous angle, we don't want to buff unless this is a wiggle type pattern.
* Math.Min(angleBonus, diameter * 1.25 / osuCurrObj.StrainTime) // The maximum velocity we buff is equal to 125 / strainTime
* Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, (100 - osuCurrObj.StrainTime) / 25)), 2) // scale buff from 150 bpm 1/4 to 200 bpm 1/4
* Math.Pow(Math.Sin(Math.PI / 2 * (Math.Clamp(osuCurrObj.LazyJumpDistance, radius, diameter) - radius) / radius), 2); // Buff distance exceeding radius up to diameter.
}

// Penalize wide angles if they're repeated, reducing the penalty as the lastAngle gets more acute.
wideAngleBonus *= angleBonus * (1 - Math.Min(wideAngleBonus, Math.Pow(calcWideAngleBonus(lastAngle), 3)));
// Penalize acute angles if they're repeated, reducing the penalty as the lastLastAngle gets more obtuse.
acuteAngleBonus *= 0.5 + 0.5 * (1 - Math.Min(acuteAngleBonus, Math.Pow(calcAcuteAngleBonus(lastLastAngle), 3)));
// Penalize angle repetition.
wideAngleBonus *= 1 - Math.Min(wideAngleBonus, Math.Pow(calcWideAngleBonus(lastAngle), 3));
acuteAngleBonus *= 0.08 + 0.92 * (1 - Math.Min(acuteAngleBonus, Math.Pow(calcAcuteAngleBonus(lastAngle), 3)));

// Apply full wide angle bonus for distance more than one diameter
wideAngleBonus *= angleBonus * DifficultyCalculationUtils.Smootherstep(osuCurrObj.LazyJumpDistance, 0, diameter);

// Apply acute angle bonus for BPM above 300 1/2 and distance more than one diameter
acuteAngleBonus *= angleBonus *
DifficultyCalculationUtils.Smootherstep(DifficultyCalculationUtils.MillisecondsToBPM(osuCurrObj.StrainTime, 2), 300, 400) *
DifficultyCalculationUtils.Smootherstep(osuCurrObj.LazyJumpDistance, diameter, diameter * 2);

// Apply wiggle bonus for jumps that are [radius, 3*diameter] in distance, with < 110 angle
// https://www.desmos.com/calculator/dp0v0nvowc
wiggleBonus = angleBonus
* DifficultyCalculationUtils.Smootherstep(osuCurrObj.LazyJumpDistance, radius, diameter)
* Math.Pow(DifficultyCalculationUtils.ReverseLerp(osuCurrObj.LazyJumpDistance, diameter * 3, diameter), 1.8)
* DifficultyCalculationUtils.Smootherstep(currAngle, double.DegreesToRadians(110), double.DegreesToRadians(60))
* DifficultyCalculationUtils.Smootherstep(osuLastObj.LazyJumpDistance, radius, diameter)
* Math.Pow(DifficultyCalculationUtils.ReverseLerp(osuLastObj.LazyJumpDistance, diameter * 3, diameter), 1.8)
* DifficultyCalculationUtils.Smootherstep(lastAngle, double.DegreesToRadians(110), double.DegreesToRadians(60));
}
}

Expand Down Expand Up @@ -122,6 +130,8 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with
sliderBonus = osuLastObj.TravelDistance / osuLastObj.TravelTime;
}

aimStrain += wiggleBonus * wiggle_multiplier;

// Add in acute angle bonus or wide angle bonus + velocity change bonus, whichever is larger.
aimStrain += Math.Max(acuteAngleBonus * acute_angle_multiplier, wideAngleBonus * wide_angle_multiplier + velocityChangeBonus * velocity_change_multiplier);

Expand All @@ -132,8 +142,8 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with
return aimStrain;
}

private static double calcWideAngleBonus(double angle) => Math.Pow(Math.Sin(3.0 / 4 * (Math.Min(5.0 / 6 * Math.PI, Math.Max(Math.PI / 6, angle)) - Math.PI / 6)), 2);
private static double calcWideAngleBonus(double angle) => DifficultyCalculationUtils.Smoothstep(angle, double.DegreesToRadians(40), double.DegreesToRadians(140));

private static double calcAcuteAngleBonus(double angle) => 1 - calcWideAngleBonus(angle);
private static double calcAcuteAngleBonus(double angle) => DifficultyCalculationUtils.Smoothstep(angle, double.DegreesToRadians(140), double.DegreesToRadians(40));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
var currentObj = (OsuDifficultyHitObject)current.Previous(i);
var currentHitObject = (OsuHitObject)(currentObj.BaseObject);

cumulativeStrainTime += lastObj.StrainTime;

if (!(currentObj.BaseObject is Spinner))
{
double jumpDistance = (osuHitObject.StackedPosition - currentHitObject.StackedEndPosition).Length;

cumulativeStrainTime += lastObj.StrainTime;

// We want to nerf objects that can be easily seen within the Flashlight circle radius.
if (i == 0)
smallDistNerf = Math.Min(1.0, jumpDistance / 75.0);
Expand Down
11 changes: 9 additions & 2 deletions osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Utils;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;

namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
Expand All @@ -14,7 +18,7 @@ public static class SpeedEvaluator
private const double single_spacing_threshold = OsuDifficultyHitObject.NORMALISED_DIAMETER * 1.25; // 1.25 circles distance between centers
private const double min_speed_bonus = 200; // 200 BPM 1/4th
private const double speed_balancing_factor = 40;
private const double distance_multiplier = 0.94;
private const double distance_multiplier = 0.9;

/// <summary>
/// Evaluates the difficulty of tapping the current object, based on:
Expand All @@ -24,7 +28,7 @@ public static class SpeedEvaluator
/// <item><description>and how easily they can be cheesed.</description></item>
/// </list>
/// </summary>
public static double EvaluateDifficultyOf(DifficultyHitObject current)
public static double EvaluateDifficultyOf(DifficultyHitObject current, IReadOnlyList<Mod> mods)
{
if (current.BaseObject is Spinner)
return 0;
Expand Down Expand Up @@ -56,6 +60,9 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current)
// Max distance bonus is 1 * `distance_multiplier` at single_spacing_threshold
double distanceBonus = Math.Pow(distance / single_spacing_threshold, 3.95) * distance_multiplier;

if (mods.OfType<OsuModAutopilot>().Any())
distanceBonus = 0;

// Base difficulty with all bonuses
double difficulty = (1 + speedBonus + distanceBonus) * 1000 / strainTime;

Expand Down
Loading
Loading