mirror of
https://github.com/ppy/osu.git
synced 2026-05-26 13:50:33 +08:00
Add spinners support to combo based estimated misscount (#33170)
* add spinner support * Make `CalculateSpinnerScore` private & clarify comments --------- Co-authored-by: James Wilson <tsunyoku@gmail.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
60eaf088df
commit
ee055ba8f5
@@ -75,8 +75,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
[JsonProperty("speed_difficult_strain_count")]
|
||||
public double SpeedDifficultStrainCount { get; set; }
|
||||
|
||||
[JsonProperty("slider_nested_score_per_object")]
|
||||
public double SliderNestedScorePerObject { get; set; }
|
||||
[JsonProperty("nested_score_per_object")]
|
||||
public double NestedScorePerObject { get; set; }
|
||||
|
||||
[JsonProperty("legacy_score_base_multiplier")]
|
||||
public double LegacyScoreBaseMultiplier { get; set; }
|
||||
@@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
yield return (ATTRIB_ID_AIM_DIFFICULT_SLIDER_COUNT, AimDifficultSliderCount);
|
||||
yield return (ATTRIB_ID_AIM_TOP_WEIGHTED_SLIDER_FACTOR, AimTopWeightedSliderFactor);
|
||||
yield return (ATTRIB_ID_SPEED_TOP_WEIGHTED_SLIDER_FACTOR, SpeedTopWeightedSliderFactor);
|
||||
yield return (ATTRIB_ID_SLIDER_NESTED_SCORE_PER_OBJECT, SliderNestedScorePerObject);
|
||||
yield return (ATTRIB_ID_NESTED_SCORE_PER_OBJECT, NestedScorePerObject);
|
||||
yield return (ATTRIB_ID_LEGACY_SCORE_BASE_MULTIPLIER, LegacyScoreBaseMultiplier);
|
||||
yield return (ATTRIB_ID_MAXIMUM_LEGACY_COMBO_SCORE, MaximumLegacyComboScore);
|
||||
}
|
||||
@@ -144,7 +144,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
AimDifficultSliderCount = values[ATTRIB_ID_AIM_DIFFICULT_SLIDER_COUNT];
|
||||
AimTopWeightedSliderFactor = values[ATTRIB_ID_AIM_TOP_WEIGHTED_SLIDER_FACTOR];
|
||||
SpeedTopWeightedSliderFactor = values[ATTRIB_ID_SPEED_TOP_WEIGHTED_SLIDER_FACTOR];
|
||||
SliderNestedScorePerObject = values[ATTRIB_ID_SLIDER_NESTED_SCORE_PER_OBJECT];
|
||||
NestedScorePerObject = values[ATTRIB_ID_NESTED_SCORE_PER_OBJECT];
|
||||
LegacyScoreBaseMultiplier = values[ATTRIB_ID_LEGACY_SCORE_BASE_MULTIPLIER];
|
||||
MaximumLegacyComboScore = values[ATTRIB_ID_MAXIMUM_LEGACY_COMBO_SCORE];
|
||||
DrainRate = onlineInfo.DrainRate;
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
? Math.Cbrt(multiplier) * star_rating_multiplier * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4)
|
||||
: 0;
|
||||
|
||||
double sliderNestedScorePerObject = LegacyScoreUtils.CalculateSliderNestedScorePerObject(beatmap, totalHits);
|
||||
double sliderNestedScorePerObject = LegacyScoreUtils.CalculateNestedScorePerObject(beatmap, totalHits);
|
||||
double legacyScoreBaseMultiplier = LegacyScoreUtils.CalculateDifficultyPeppyStars(beatmap);
|
||||
|
||||
var simulator = new OsuLegacyScoreSimulator();
|
||||
@@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
HitCircleCount = hitCircleCount,
|
||||
SliderCount = sliderCount,
|
||||
SpinnerCount = spinnerCount,
|
||||
SliderNestedScorePerObject = sliderNestedScorePerObject,
|
||||
NestedScorePerObject = sliderNestedScorePerObject,
|
||||
LegacyScoreBaseMultiplier = legacyScoreBaseMultiplier,
|
||||
MaximumLegacyComboScore = scoreAttributes.ComboScore
|
||||
};
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
double objectsHit = (totalHits - countMiss) * combo / attributes.MaxCombo;
|
||||
|
||||
// Score also has a non-combo portion we need to create the final score value.
|
||||
double nonComboScore = (300 + attributes.SliderNestedScorePerObject) * score.Accuracy * objectsHit;
|
||||
double nonComboScore = (300 + attributes.NestedScorePerObject) * score.Accuracy * objectsHit;
|
||||
|
||||
return comboScore + nonComboScore;
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Utils
|
||||
public static class LegacyScoreUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculates the average amount of score per object that is caused by slider ticks.
|
||||
/// Calculates the average amount of score per object that is caused by nested judgements such as slider-ticks and spinners.
|
||||
/// </summary>
|
||||
public static double CalculateSliderNestedScorePerObject(IBeatmap beatmap, int objectCount)
|
||||
public static double CalculateNestedScorePerObject(IBeatmap beatmap, int objectCount)
|
||||
{
|
||||
const double big_tick_score = 30;
|
||||
const double small_tick_score = 10;
|
||||
@@ -29,9 +29,59 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Utils
|
||||
|
||||
int amountOfSmallTicks = sliders.Select(s => s.NestedHitObjects.Count(nho => nho is SliderTick)).Sum();
|
||||
|
||||
double totalScore = amountOfBigTicks * big_tick_score + amountOfSmallTicks * small_tick_score;
|
||||
double sliderScore = amountOfBigTicks * big_tick_score + amountOfSmallTicks * small_tick_score;
|
||||
|
||||
return totalScore / objectCount;
|
||||
double spinnerScore = 0;
|
||||
|
||||
foreach (var spinner in beatmap.HitObjects.OfType<Spinner>())
|
||||
{
|
||||
spinnerScore += calculateSpinnerScore(spinner);
|
||||
}
|
||||
|
||||
return (sliderScore + spinnerScore) / objectCount;
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Logic borrowed from <see cref="OsuLegacyScoreSimulator.simulateHit"/> for basic score calculations.
|
||||
/// </remarks>
|
||||
private static double calculateSpinnerScore(Spinner spinner)
|
||||
{
|
||||
const int spin_score = 100;
|
||||
const int bonus_spin_score = 1000;
|
||||
|
||||
// The spinner object applies a lenience because gameplay mechanics differ from osu-stable.
|
||||
// We'll redo the calculations to match osu-stable here...
|
||||
const double maximum_rotations_per_second = 477.0 / 60;
|
||||
|
||||
// Normally, this value depends on the final overall difficulty. For simplicity, we'll only consider the worst case that maximises bonus score.
|
||||
// As we're primarily concerned with computing the maximum theoretical final score,
|
||||
// this will have the final effect of slightly underestimating bonus score achieved on stable when converting from score V1.
|
||||
const double minimum_rotations_per_second = 3;
|
||||
|
||||
double secondsDuration = spinner.Duration / 1000;
|
||||
|
||||
// The total amount of half spins possible for the entire spinner.
|
||||
int totalHalfSpinsPossible = (int)(secondsDuration * maximum_rotations_per_second * 2);
|
||||
// The amount of half spins that are required to successfully complete the spinner (i.e. get a 300).
|
||||
int halfSpinsRequiredForCompletion = (int)(secondsDuration * minimum_rotations_per_second);
|
||||
// To be able to receive bonus points, the spinner must be rotated another 1.5 times.
|
||||
int halfSpinsRequiredBeforeBonus = halfSpinsRequiredForCompletion + 3;
|
||||
|
||||
long score = 0;
|
||||
|
||||
int fullSpins = (totalHalfSpinsPossible / 2);
|
||||
|
||||
// Normal spin score
|
||||
score += spin_score * fullSpins;
|
||||
|
||||
int bonusSpins = (totalHalfSpinsPossible - halfSpinsRequiredBeforeBonus) / 2;
|
||||
|
||||
// Reduce amount of bonus spins because we want to represent the more average case, rather than the best one.
|
||||
bonusSpins = Math.Max(0, bonusSpins - fullSpins / 2);
|
||||
|
||||
score += bonus_spin_score * bonusSpins;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
public static int CalculateDifficultyPeppyStars(IBeatmap beatmap)
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
protected const int ATTRIB_ID_AIM_DIFFICULT_SLIDER_COUNT = 31;
|
||||
protected const int ATTRIB_ID_AIM_TOP_WEIGHTED_SLIDER_FACTOR = 33;
|
||||
protected const int ATTRIB_ID_SPEED_TOP_WEIGHTED_SLIDER_FACTOR = 35;
|
||||
protected const int ATTRIB_ID_SLIDER_NESTED_SCORE_PER_OBJECT = 37;
|
||||
protected const int ATTRIB_ID_NESTED_SCORE_PER_OBJECT = 37;
|
||||
protected const int ATTRIB_ID_LEGACY_SCORE_BASE_MULTIPLIER = 39;
|
||||
protected const int ATTRIB_ID_MAXIMUM_LEGACY_COMBO_SCORE = 41;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user