diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
index bf5c3a91d6..9c5359ebeb 100644
--- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
@@ -31,13 +31,11 @@ namespace osu.Game.Rulesets.Catch.Scoring
const int tiny_droplets_portion = 400000;
- return
- (int)Math.Round
- ((
- ((1000000 - tiny_droplets_portion) + tiny_droplets_portion * (1 - tinyDropletScale)) * ComboPortion / MaxComboPortion +
- tiny_droplets_portion * tinyDropletScale * fruitHitsRatio +
- BonusPortion
- ) * ScoreMultiplier);
+ return (
+ ((1000000 - tiny_droplets_portion) + tiny_droplets_portion * (1 - tinyDropletScale)) * ComboPortion / MaxComboPortion +
+ tiny_droplets_portion * tinyDropletScale * fruitHitsRatio +
+ BonusPortion
+ ) * ScoreMultiplier;
}
protected override void AddScoreChange(JudgementResult result)
diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
index 7d188c6786..19f8a4a639 100644
--- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
+++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
@@ -20,13 +20,11 @@ namespace osu.Game.Rulesets.Mania.Scoring
protected override double ComputeTotalScore()
{
- return
- (int)Math.Round
- ((
- 200000 * ComboPortion / MaxComboPortion +
- 800000 * Math.Pow(Accuracy.Value, 2 + 2 * Accuracy.Value) * ((double)CurrentBasicJudgements / MaxBasicJudgements) +
- BonusPortion
- ) * ScoreMultiplier);
+ return (
+ 200000 * ComboPortion / MaxComboPortion +
+ 800000 * Math.Pow(Accuracy.Value, 2 + 2 * Accuracy.Value) * ((double)CurrentBasicJudgements / MaxBasicJudgements) +
+ BonusPortion
+ ) * ScoreMultiplier;
}
protected override void AddScoreChange(JudgementResult result)
diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
index 5f5997b0c1..f8cbf1a641 100644
--- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
@@ -17,13 +17,11 @@ namespace osu.Game.Rulesets.Osu.Scoring
protected override double ComputeTotalScore()
{
- return
- (int)Math.Round
- ((
- 700000 * ComboPortion / MaxComboPortion +
- 300000 * Math.Pow(Accuracy.Value, 10) * ((double)CurrentBasicJudgements / MaxBasicJudgements) +
- BonusPortion
- ) * ScoreMultiplier);
+ return (
+ 700000 * ComboPortion / MaxComboPortion +
+ 300000 * Math.Pow(Accuracy.Value, 10) * ((double)CurrentBasicJudgements / MaxBasicJudgements) +
+ BonusPortion
+ ) * ScoreMultiplier;
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
index caf0a799a2..71eb0b1602 100644
--- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
+++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
@@ -21,13 +21,11 @@ namespace osu.Game.Rulesets.Taiko.Scoring
protected override double ComputeTotalScore()
{
- return
- (int)Math.Round
- ((
- 250000 * ComboPortion / MaxComboPortion +
- 750000 * Math.Pow(Accuracy.Value, 3.6) * ((double)CurrentBasicJudgements / MaxBasicJudgements) +
- BonusPortion
- ) * ScoreMultiplier);
+ return (
+ 250000 * ComboPortion / MaxComboPortion +
+ 750000 * Math.Pow(Accuracy.Value, 3.6) * ((double)CurrentBasicJudgements / MaxBasicJudgements) +
+ BonusPortion
+ ) * ScoreMultiplier;
}
protected override void AddScoreChange(JudgementResult result)
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index 8373ebd50c..9172034ff6 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -274,8 +274,20 @@ namespace osu.Game.Rulesets.Scoring
{
Accuracy.Value = currentMaxBasicScore > 0 ? currentBasicScore / currentMaxBasicScore : 1;
- // Todo: Classic/Standardised
- TotalScore.Value = (long)Math.Round(ComputeTotalScore());
+ double standardisedScore = ComputeTotalScore();
+
+ if (Mode.Value == ScoringMode.Standardised)
+ TotalScore.Value = (long)Math.Round(standardisedScore);
+ else
+ TotalScore.Value = ConvertToClassic(standardisedScore);
+ }
+
+ public long ConvertToClassic(double standardised)
+ {
+ // This gives a similar feeling to osu!stable scoring (ScoreV1) while keeping classic scoring as only a constant multiple of standardised scoring.
+ // The invariant is important to ensure that scores don't get re-ordered on leaderboards between the two scoring modes.
+ double scaledRawScore = standardised / MAX_SCORE;
+ return (long)Math.Round(Math.Pow(scaledRawScore * Math.Max(1, MaxBasicJudgements), 2) * ClassicScoreMultiplier);
}
protected abstract double ComputeTotalScore();
diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs
index 3779156fda..0674947f30 100644
--- a/osu.Game/Scoring/ScoreManager.cs
+++ b/osu.Game/Scoring/ScoreManager.cs
@@ -107,17 +107,15 @@ namespace osu.Game.Scoring
/// The total score.
public long GetTotalScore([NotNull] ScoreInfo score, ScoringMode mode = ScoringMode.Standardised)
{
- // TODO: This is required for playlist aggregate scores. They should likely not be getting here in the first place.
- if (string.IsNullOrEmpty(score.BeatmapInfo.MD5Hash))
+ if (mode == ScoringMode.Standardised)
return score.TotalScore;
var ruleset = score.Ruleset.CreateInstance();
var scoreProcessor = ruleset.CreateScoreProcessor();
scoreProcessor.Mods.Value = score.Mods;
- // Todo:
- return 0;
- // return scoreProcessor.ComputeScore(mode, score);
+ // Todo: This loses precision because we're dealing with pre-rounded total scores.
+ return scoreProcessor.ConvertToClassic(score.TotalScore);
}
///