1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-12 19:42:55 +08:00

Combine mania skills

This commit is contained in:
smoogipoo 2020-10-09 21:47:34 +09:00
parent 8f37d2290a
commit 5017c92fe8
4 changed files with 84 additions and 146 deletions

View File

@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
return new ManiaDifficultyAttributes return new ManiaDifficultyAttributes
{ {
StarRating = difficultyValue(skills) * star_scaling_factor, StarRating = skills[0].DifficultyValue() * star_scaling_factor,
Mods = mods, Mods = mods,
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
GreatHitWindow = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate, GreatHitWindow = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate,
@ -49,55 +49,16 @@ namespace osu.Game.Rulesets.Mania.Difficulty
}; };
} }
private double difficultyValue(Skill[] skills)
{
// Preprocess the strains to find the maximum overall + individual (aggregate) strain from each section
var overall = skills.OfType<Overall>().Single();
var aggregatePeaks = new List<double>(Enumerable.Repeat(0.0, overall.StrainPeaks.Count));
foreach (var individual in skills.OfType<Individual>())
{
for (int i = 0; i < individual.StrainPeaks.Count; i++)
{
double aggregate = individual.StrainPeaks[i] + overall.StrainPeaks[i];
if (aggregate > aggregatePeaks[i])
aggregatePeaks[i] = aggregate;
}
}
aggregatePeaks.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
double difficulty = 0;
double weight = 1;
// Difficulty is the weighted sum of the highest strains from every section.
foreach (double strain in aggregatePeaks)
{
difficulty += strain * weight;
weight *= 0.9;
}
return difficulty;
}
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
{ {
for (int i = 1; i < beatmap.HitObjects.Count; i++) for (int i = 1; i < beatmap.HitObjects.Count; i++)
yield return new ManiaDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], clockRate); yield return new ManiaDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], clockRate);
} }
protected override Skill[] CreateSkills(IBeatmap beatmap) protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[]
{ {
int columnCount = ((ManiaBeatmap)beatmap).TotalColumns; new Strain(((ManiaBeatmap)beatmap).TotalColumns)
};
var skills = new List<Skill> { new Overall(columnCount) };
for (int i = 0; i < columnCount; i++)
skills.Add(new Individual(i, columnCount));
return skills.ToArray();
}
protected override Mod[] DifficultyAdjustmentMods protected override Mod[] DifficultyAdjustmentMods
{ {

View File

@ -1,47 +0,0 @@
// 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.Linq;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
{
public class Individual : Skill
{
protected override double SkillMultiplier => 1;
protected override double StrainDecayBase => 0.125;
private readonly double[] holdEndTimes;
private readonly int column;
public Individual(int column, int columnCount)
{
this.column = column;
holdEndTimes = new double[columnCount];
}
protected override double StrainValueOf(DifficultyHitObject current)
{
var maniaCurrent = (ManiaDifficultyHitObject)current;
var endTime = maniaCurrent.BaseObject.GetEndTime();
try
{
if (maniaCurrent.BaseObject.Column != column)
return 0;
// We give a slight bonus if something is held meanwhile
return holdEndTimes.Any(t => t > endTime) ? 2.5 : 2;
}
finally
{
holdEndTimes[maniaCurrent.BaseObject.Column] = endTime;
}
}
}
}

View File

@ -1,56 +0,0 @@
// 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 osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
{
public class Overall : Skill
{
protected override double SkillMultiplier => 1;
protected override double StrainDecayBase => 0.3;
private readonly double[] holdEndTimes;
private readonly int columnCount;
public Overall(int columnCount)
{
this.columnCount = columnCount;
holdEndTimes = new double[columnCount];
}
protected override double StrainValueOf(DifficultyHitObject current)
{
var maniaCurrent = (ManiaDifficultyHitObject)current;
var endTime = maniaCurrent.BaseObject.GetEndTime();
double holdFactor = 1.0; // Factor in case something else is held
double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly
for (int i = 0; i < columnCount; i++)
{
// If there is at least one other overlapping end or note, then we get an addition, buuuuuut...
if (current.BaseObject.StartTime < holdEndTimes[i] && endTime > holdEndTimes[i])
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 one
if (endTime == holdEndTimes[i])
holdAddition = 0;
// We give a slight bonus if something is held meanwhile
if (holdEndTimes[i] > endTime)
holdFactor = 1.25;
}
holdEndTimes[maniaCurrent.BaseObject.Column] = endTime;
return (1 + holdAddition) * holdFactor;
}
}
}

View File

@ -0,0 +1,80 @@
// 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 osu.Framework.Utils;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
{
public class Strain : Skill
{
private const double individual_decay_base = 0.125;
private const double overall_decay_base = 0.30;
protected override double SkillMultiplier => 1;
protected override double StrainDecayBase => 1;
private readonly double[] holdEndTimes;
private readonly double[] individualStrains;
private double individualStrain;
private double overallStrain;
public Strain(int totalColumns)
{
holdEndTimes = new double[totalColumns];
individualStrains = new double[totalColumns];
overallStrain = 1;
}
protected override double StrainValueOf(DifficultyHitObject current)
{
var maniaCurrent = (ManiaDifficultyHitObject)current;
var endTime = maniaCurrent.BaseObject.GetEndTime();
var column = maniaCurrent.BaseObject.Column;
double holdFactor = 1.0; // Factor to all additional strains in case something else is held
double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly
// Fill up the holdEndTimes array
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))
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
if (Precision.AlmostEquals(endTime, holdEndTimes[i], 1))
holdAddition = 0;
// We give a slight bonus to everything if something is held meanwhile
if (Precision.DefinitelyBigger(holdEndTimes[i], endTime, 1))
holdFactor = 1.25;
// Decay individual strains
individualStrains[i] = applyDecay(individualStrains[i], current.DeltaTime, individual_decay_base);
}
holdEndTimes[column] = endTime;
// Increase individual strain in own column
individualStrains[column] += 2.0 * holdFactor;
individualStrain = individualStrains[column];
overallStrain = applyDecay(overallStrain, current.DeltaTime, overall_decay_base) + (1 + holdAddition) * holdFactor;
return individualStrain + overallStrain - CurrentStrain;
}
protected override double GetPeakStrain(double offset)
=> applyDecay(individualStrain, offset - Previous[0].BaseObject.StartTime, individual_decay_base)
+ applyDecay(overallStrain, offset - Previous[0].BaseObject.StartTime, overall_decay_base);
private double applyDecay(double value, double deltaTime, double decayBase)
=> value * Math.Pow(decayBase, deltaTime / 1000);
}
}