1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-17 14:23:20 +08:00
osu-lazer/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
Bartłomiej Dach 1af464d5ae
Bump difficulty calculator versions
In order for the new star difficulty to be shown to users on the next
release.

catch's difficulty calculator version is not bumped because the only
catch change pending deploy is https://github.com/ppy/osu/pull/28353 and
that affects performance points only.
2024-10-07 15:38:41 +02:00

162 lines
6.5 KiB
C#

// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Difficulty.Skills;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Difficulty
{
public class OsuDifficultyCalculator : DifficultyCalculator
{
private const double difficulty_multiplier = 0.0675;
public override int Version => 20241007;
public OsuDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
if (beatmap.HitObjects.Count == 0)
return new OsuDifficultyAttributes { Mods = mods };
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
double aimRatingNoSliders = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
double speedRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier;
double speedNotes = ((Speed)skills[2]).RelevantNoteCount();
double flashlightRating = 0.0;
if (mods.Any(h => h is OsuModFlashlight))
flashlightRating = Math.Sqrt(skills[3].DifficultyValue()) * difficulty_multiplier;
double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
double aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).CountDifficultStrains();
double speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).CountDifficultStrains();
if (mods.Any(m => m is OsuModTouchDevice))
{
aimRating = Math.Pow(aimRating, 0.8);
flashlightRating = Math.Pow(flashlightRating, 0.8);
}
if (mods.Any(h => h is OsuModRelax))
{
aimRating *= 0.9;
speedRating = 0.0;
flashlightRating *= 0.7;
}
double baseAimPerformance = OsuStrainSkill.DifficultyToPerformance(aimRating);
double baseSpeedPerformance = OsuStrainSkill.DifficultyToPerformance(speedRating);
double baseFlashlightPerformance = 0.0;
if (mods.Any(h => h is OsuModFlashlight))
baseFlashlightPerformance = Flashlight.DifficultyToPerformance(flashlightRating);
double basePerformance =
Math.Pow(
Math.Pow(baseAimPerformance, 1.1) +
Math.Pow(baseSpeedPerformance, 1.1) +
Math.Pow(baseFlashlightPerformance, 1.1), 1.0 / 1.1
);
double starRating = basePerformance > 0.00001
? Math.Cbrt(OsuPerformanceCalculator.PERFORMANCE_BASE_MULTIPLIER) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4)
: 0;
double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate;
double drainRate = beatmap.Difficulty.DrainRate;
int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle);
int sliderCount = beatmap.HitObjects.Count(h => h is Slider);
int spinnerCount = beatmap.HitObjects.Count(h => h is Spinner);
HitWindows hitWindows = new OsuHitWindows();
hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);
double hitWindowGreat = hitWindows.WindowFor(HitResult.Great) / clockRate;
OsuDifficultyAttributes attributes = new OsuDifficultyAttributes
{
StarRating = starRating,
Mods = mods,
AimDifficulty = aimRating,
SpeedDifficulty = speedRating,
SpeedNoteCount = speedNotes,
FlashlightDifficulty = flashlightRating,
SliderFactor = sliderFactor,
AimDifficultStrainCount = aimDifficultyStrainCount,
SpeedDifficultStrainCount = speedDifficultyStrainCount,
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
OverallDifficulty = (80 - hitWindowGreat) / 6,
DrainRate = drainRate,
MaxCombo = beatmap.GetMaxCombo(),
HitCircleCount = hitCirclesCount,
SliderCount = sliderCount,
SpinnerCount = spinnerCount,
};
return attributes;
}
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
{
List<DifficultyHitObject> objects = new List<DifficultyHitObject>();
// The first jump is formed by the first two hitobjects of the map.
// If the map has less than two OsuHitObjects, the enumerator will not return anything.
for (int i = 1; i < beatmap.HitObjects.Count; i++)
{
var lastLast = i > 1 ? beatmap.HitObjects[i - 2] : null;
objects.Add(new OsuDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], lastLast, clockRate, objects, objects.Count));
}
return objects;
}
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate)
{
var skills = new List<Skill>
{
new Aim(mods, true),
new Aim(mods, false),
new Speed(mods)
};
if (mods.Any(h => h is OsuModFlashlight))
skills.Add(new Flashlight(mods));
return skills.ToArray();
}
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
{
new OsuModTouchDevice(),
new OsuModDoubleTime(),
new OsuModHalfTime(),
new OsuModEasy(),
new OsuModHardRock(),
new OsuModFlashlight(),
new MultiMod(new OsuModFlashlight(), new OsuModHidden())
};
}
}