1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-09 22:33:57 +08:00
Files
osu-lazer/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs
T
StanR fadb1c3f2c Calculate clock time when creating difficulty hit objects (#36962)
This change removes `clockRate` precalculation from
`DifficultyCalculator`.

The idea is that clock rate should be calculated in-place (ideally for
every object) since we store and access it using DHOs. This also
prevents anyone from accidentally passing clock rate to skills

Unfortunately osu uses clock rate to calculate OD for the whole map in
`CreateDifficultyAttributes` so we can't make it completely DHO-based,
but I think one single in-place call to `ModUtils.CalculateRateWithMods`
in `CreateDifficultyAttributes` is fine

---------

Co-authored-by: James Wilson <tsunyoku@gmail.com>
2026-03-16 21:17:49 +00:00

136 lines
5.2 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.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Extensions;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
using osu.Game.Rulesets.Mania.Difficulty.Skills;
using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Utils;
namespace osu.Game.Rulesets.Mania.Difficulty
{
public class ManiaDifficultyCalculator : DifficultyCalculator
{
private const double difficulty_multiplier = 0.018;
private readonly bool isForCurrentRuleset;
public override int Version => 20241007;
public ManiaDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.MatchesOnlineID(ruleset);
}
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills)
{
if (beatmap.HitObjects.Count == 0)
return new ManiaDifficultyAttributes { Mods = mods };
HitWindows hitWindows = new ManiaHitWindows();
hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);
ManiaDifficultyAttributes attributes = new ManiaDifficultyAttributes
{
StarRating = skills.OfType<Strain>().Single().DifficultyValue() * difficulty_multiplier,
Mods = mods,
MaxCombo = beatmap.HitObjects.Sum(maxComboForObject),
};
return attributes;
}
private static int maxComboForObject(HitObject hitObject)
{
if (hitObject is HoldNote hold)
return 1 + (int)((hold.EndTime - hold.StartTime) / 100);
return 1;
}
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, Mod[] mods)
{
var sortedObjects = beatmap.HitObjects.ToArray();
int totalColumns = ((ManiaBeatmap)beatmap).TotalColumns;
double clockRate = ModUtils.CalculateRateWithMods(mods);
LegacySortHelper<HitObject>.Sort(sortedObjects, Comparer<HitObject>.Create((a, b) => (int)Math.Round(a.StartTime) - (int)Math.Round(b.StartTime)));
List<DifficultyHitObject> objects = new List<DifficultyHitObject>();
List<DifficultyHitObject>[] perColumnObjects = new List<DifficultyHitObject>[totalColumns];
for (int column = 0; column < totalColumns; column++)
perColumnObjects[column] = new List<DifficultyHitObject>();
for (int i = 1; i < sortedObjects.Length; i++)
{
var currentObject = new ManiaDifficultyHitObject(sortedObjects[i], sortedObjects[i - 1], clockRate, objects, perColumnObjects, objects.Count);
objects.Add(currentObject);
perColumnObjects[currentObject.Column].Add(currentObject);
}
return objects;
}
// Sorting is done in CreateDifficultyHitObjects, since the full list of hitobjects is required.
protected override IEnumerable<DifficultyHitObject> SortObjects(IEnumerable<DifficultyHitObject> input) => input;
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[]
{
new Strain(mods, ((ManiaBeatmap)Beatmap).TotalColumns)
};
protected override Mod[] DifficultyAdjustmentMods
{
get
{
var mods = new Mod[]
{
new ManiaModDoubleTime(),
new ManiaModHalfTime(),
new ManiaModEasy(),
new ManiaModHardRock(),
};
if (isForCurrentRuleset)
return mods;
// if we are a convert, we can be played in any key mod.
return mods.Concat(new Mod[]
{
new ManiaModKey1(),
new ManiaModKey2(),
new ManiaModKey3(),
new ManiaModKey4(),
new ManiaModKey5(),
new MultiMod(new ManiaModKey5(), new ManiaModDualStages()),
new ManiaModKey6(),
new MultiMod(new ManiaModKey6(), new ManiaModDualStages()),
new ManiaModKey7(),
new MultiMod(new ManiaModKey7(), new ManiaModDualStages()),
new ManiaModKey8(),
new MultiMod(new ManiaModKey8(), new ManiaModDualStages()),
new ManiaModKey9(),
new MultiMod(new ManiaModKey9(), new ManiaModDualStages()),
}).ToArray();
}
}
}
}