diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs index 7a84d9245d..f65b6ef381 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs @@ -7,7 +7,7 @@ using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Mods; using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Catch.Scoring; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty { internal class CatchLegacyScoreSimulator : ILegacyScoreSimulator { + private readonly ScoreProcessor scoreProcessor = new CatchScoreProcessor(); + private int legacyBonusScore; private int standardisedBonusScore; private int combo; @@ -134,7 +136,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty if (isBonus) { legacyBonusScore += scoreIncrease; - standardisedBonusScore += Judgement.ToNumericResult(bonusResult); + standardisedBonusScore += scoreProcessor.GetBaseScoreForResult(bonusResult); } else attributes.AccuracyScore += scoreIncrease; diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 503252df02..4b3d378889 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Scoring } protected override double GetComboScoreChange(JudgementResult result) - => GetNumericResultFor(result) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(combo_cap, combo_base)); + => GetBaseScoreForResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(combo_cap, combo_base)); public override ScoreRank RankFromAccuracy(double accuracy) { diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index d5191c880a..1947d86a97 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -31,44 +31,31 @@ namespace osu.Game.Rulesets.Mania.Scoring + bonusPortion; } - protected override double GetNumericResultFor(JudgementResult result) - { - switch (result.Type) - { - case HitResult.Perfect: - return 305; - } - - return base.GetNumericResultFor(result); - } - - protected override double GetMaxNumericResultFor(JudgementResult result) - { - switch (result.Judgement.MaxResult) - { - case HitResult.Perfect: - return 305; - } - - return base.GetMaxNumericResultFor(result); - } - protected override double GetComboScoreChange(JudgementResult result) { - double numericResult; + return getBaseComboScoreForResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base)); + } - switch (result.Type) + public override int GetBaseScoreForResult(HitResult result) + { + switch (result) { case HitResult.Perfect: - numericResult = 300; - break; - - default: - numericResult = GetNumericResultFor(result); - break; + return 305; } - return numericResult * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base)); + return base.GetBaseScoreForResult(result); + } + + private int getBaseComboScoreForResult(HitResult result) + { + switch (result) + { + case HitResult.Perfect: + return 300; + } + + return GetBaseScoreForResult(result); } private class JudgementOrderComparer : IComparer diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index 8711aa9c09..6706d20080 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -58,10 +58,7 @@ namespace osu.Game.Rulesets.Osu.Tests double trackerRotationTolerance = 0; addSeekStep(5000); - AddStep("calculate rotation tolerance", () => - { - trackerRotationTolerance = Math.Abs(drawableSpinner.RotationTracker.Rotation * 0.1f); - }); + AddStep("calculate rotation tolerance", () => { trackerRotationTolerance = Math.Abs(drawableSpinner.RotationTracker.Rotation * 0.1f); }); AddAssert("is disc rotation not almost 0", () => drawableSpinner.RotationTracker.Rotation, () => Is.Not.EqualTo(0).Within(100)); AddAssert("is disc rotation absolute not almost 0", () => drawableSpinner.Result.TotalRotation, () => Is.Not.EqualTo(0).Within(100)); @@ -133,9 +130,11 @@ namespace osu.Game.Rulesets.Osu.Tests AddAssert("player score matching expected bonus score", () => { + var scoreProcessor = ((ScoreExposedPlayer)Player).ScoreProcessor; + // multipled by 2 to nullify the score multiplier. (autoplay mod selected) - long totalScore = ((ScoreExposedPlayer)Player).ScoreProcessor.TotalScore.Value * 2; - return totalScore == (int)(drawableSpinner.Result.TotalRotation / 360) * new SpinnerTick().CreateJudgement().MaxNumericResult; + long totalScore = scoreProcessor.TotalScore.Value * 2; + return totalScore == (int)(drawableSpinner.Result.TotalRotation / 360) * scoreProcessor.GetBaseScoreForResult(new SpinnerTick().CreateJudgement().MaxResult); }); addSeekStep(0); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs index 28967cbf7e..a76054b42c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs @@ -5,12 +5,12 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring.Legacy; @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty { internal class OsuLegacyScoreSimulator : ILegacyScoreSimulator { + private readonly ScoreProcessor scoreProcessor = new OsuScoreProcessor(); + private int legacyBonusScore; private int standardisedBonusScore; private int combo; @@ -171,7 +173,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (isBonus) { legacyBonusScore += scoreIncrease; - standardisedBonusScore += Judgement.ToNumericResult(bonusResult); + standardisedBonusScore += scoreProcessor.GetBaseScoreForResult(bonusResult); } else attributes.AccuracyScore += scoreIncrease; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index c0c135d145..f7c1437009 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Rulesets.Scoring; @@ -312,7 +313,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables updateBonusScore(); } - private static readonly int score_per_tick = new SpinnerBonusTick.OsuSpinnerBonusTickJudgement().MaxNumericResult; + private static readonly int score_per_tick = new OsuScoreProcessor().GetBaseScoreForResult(new SpinnerBonusTick.OsuSpinnerBonusTickJudgement().MaxResult); private void updateBonusScore() { diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs index a8ed056c89..b20aa4f2b6 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -13,11 +12,14 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring.Legacy; using osu.Game.Rulesets.Taiko.Mods; using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Scoring; namespace osu.Game.Rulesets.Taiko.Difficulty { internal class TaikoLegacyScoreSimulator : ILegacyScoreSimulator { + private readonly ScoreProcessor scoreProcessor = new TaikoScoreProcessor(); + private int legacyBonusScore; private int standardisedBonusScore; private int combo; @@ -191,7 +193,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty if (isBonus) { legacyBonusScore += scoreIncrease; - standardisedBonusScore += Judgement.ToNumericResult(bonusResult); + standardisedBonusScore += scoreProcessor.GetBaseScoreForResult(bonusResult); } else attributes.AccuracyScore += scoreIncrease; diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index bc1e42c5d1..2fd9f070ec 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -28,20 +28,20 @@ namespace osu.Game.Rulesets.Taiko.Scoring protected override double GetComboScoreChange(JudgementResult result) { - return GetNumericResultFor(result) + return GetBaseScoreForResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base)) * strongScaleValue(result); } - protected override double GetNumericResultFor(JudgementResult result) + public override int GetBaseScoreForResult(HitResult result) { - switch (result.Type) + switch (result) { case HitResult.Ok: return 150; } - return base.GetNumericResultFor(result); + return base.GetBaseScoreForResult(result); } private double strongScaleValue(JudgementResult result) diff --git a/osu.Game.Tests/Gameplay/TestSceneScoreProcessor.cs b/osu.Game.Tests/Gameplay/TestSceneScoreProcessor.cs index 1cf72cf937..1a644ad600 100644 --- a/osu.Game.Tests/Gameplay/TestSceneScoreProcessor.cs +++ b/osu.Game.Tests/Gameplay/TestSceneScoreProcessor.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Gameplay // Apply a judgement scoreProcessor.ApplyResult(new JudgementResult(new HitObject(), new TestJudgement(HitResult.LargeBonus)) { Type = HitResult.LargeBonus }); - Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(Judgement.LARGE_BONUS_SCORE)); + Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(scoreProcessor.GetBaseScoreForResult(HitResult.LargeBonus))); } [Test] diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index 8f6dced350..a748a7422a 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -340,15 +340,12 @@ namespace osu.Game try { - var score = scoreManager.Query(s => s.ID == id); - long newTotalScore = StandardisedScoreMigrationTools.ConvertFromLegacyTotalScore(score, beatmapManager); - // Can't use async overload because we're not on the update thread. // ReSharper disable once MethodHasAsyncOverload realmAccess.Write(r => { ScoreInfo s = r.Find(id)!; - s.TotalScore = newTotalScore; + StandardisedScoreMigrationTools.UpdateFromLegacy(s, beatmapManager); s.TotalScoreVersion = LegacyScoreEncoder.LATEST_VERSION; }); diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 2e5ca390d7..380bf63e63 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -57,14 +57,14 @@ namespace osu.Game.Database // We are constructing a "best possible" score from the statistics provided because it's the best we can do. List sortedHits = score.Statistics .Where(kvp => kvp.Key.AffectsCombo()) - .OrderByDescending(kvp => Judgement.ToNumericResult(kvp.Key)) + .OrderByDescending(kvp => processor.GetBaseScoreForResult(kvp.Key)) .SelectMany(kvp => Enumerable.Repeat(kvp.Key, kvp.Value)) .ToList(); // Attempt to use maximum statistics from the database. var maximumJudgements = score.MaximumStatistics .Where(kvp => kvp.Key.AffectsCombo()) - .OrderByDescending(kvp => Judgement.ToNumericResult(kvp.Key)) + .OrderByDescending(kvp => processor.GetBaseScoreForResult(kvp.Key)) .SelectMany(kvp => Enumerable.Repeat(new FakeJudgement(kvp.Key), kvp.Value)) .ToList(); @@ -169,10 +169,10 @@ namespace osu.Game.Database public static long GetOldStandardised(ScoreInfo score) { double accuracyScore = - (double)score.Statistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value) - / score.MaximumStatistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value); + (double)score.Statistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => numericScoreFor(kvp.Key) * kvp.Value) + / score.MaximumStatistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => numericScoreFor(kvp.Key) * kvp.Value); double comboScore = (double)score.MaxCombo / score.MaximumStatistics.Where(kvp => kvp.Key.AffectsCombo()).Sum(kvp => kvp.Value); - double bonusScore = score.Statistics.Where(kvp => kvp.Key.IsBonus()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value); + double bonusScore = score.Statistics.Where(kvp => kvp.Key.IsBonus()).Sum(kvp => numericScoreFor(kvp.Key) * kvp.Value); double accuracyPortion = 0.3; @@ -193,6 +193,65 @@ namespace osu.Game.Database modMultiplier *= mod.ScoreMultiplier; return (long)Math.Round((1000000 * (accuracyPortion * accuracyScore + (1 - accuracyPortion) * comboScore) + bonusScore) * modMultiplier); + + static int numericScoreFor(HitResult result) + { + switch (result) + { + default: + return 0; + + case HitResult.SmallTickHit: + return 10; + + case HitResult.LargeTickHit: + return 30; + + case HitResult.Meh: + return 50; + + case HitResult.Ok: + return 100; + + case HitResult.Good: + return 200; + + case HitResult.Great: + return 300; + + case HitResult.Perfect: + return 315; + + case HitResult.SmallBonus: + return 10; + + case HitResult.LargeBonus: + return 50; + } + } + } + + /// + /// Updates a legacy to standardised scoring. + /// + /// The score to update. + /// A used for lookups. + public static void UpdateFromLegacy(ScoreInfo score, BeatmapManager beatmaps) + { + score.TotalScore = convertFromLegacyTotalScore(score, beatmaps); + score.Accuracy = ComputeAccuracy(score); + } + + /// + /// Updates a legacy to standardised scoring. + /// + /// The score to update. + /// The beatmap difficulty. + /// The legacy scoring attributes for the beatmap which the score was set on. + public static void UpdateFromLegacy(ScoreInfo score, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes) + { + score.TotalScore = convertFromLegacyTotalScore(score, difficulty, attributes); + score.Accuracy = ComputeAccuracy(score); } /// @@ -201,7 +260,7 @@ namespace osu.Game.Database /// The score to convert the total score of. /// A used for lookups. /// The standardised total score. - public static long ConvertFromLegacyTotalScore(ScoreInfo score, BeatmapManager beatmaps) + private static long convertFromLegacyTotalScore(ScoreInfo score, BeatmapManager beatmaps) { if (!score.IsLegacyScore) return score.TotalScore; @@ -224,7 +283,7 @@ namespace osu.Game.Database ILegacyScoreSimulator sv1Simulator = legacyRuleset.CreateLegacyScoreSimulator(); LegacyScoreAttributes attributes = sv1Simulator.Simulate(beatmap, playableBeatmap); - return ConvertFromLegacyTotalScore(score, LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap.Beatmap), attributes); + return convertFromLegacyTotalScore(score, LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap.Beatmap), attributes); } /// @@ -234,7 +293,7 @@ namespace osu.Game.Database /// The beatmap difficulty. /// The legacy scoring attributes for the beatmap which the score was set on. /// The standardised total score. - public static long ConvertFromLegacyTotalScore(ScoreInfo score, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes) + private static long convertFromLegacyTotalScore(ScoreInfo score, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes) { if (!score.IsLegacyScore) return score.TotalScore; @@ -386,6 +445,19 @@ namespace osu.Game.Database } } + public static double ComputeAccuracy(ScoreInfo scoreInfo) + { + Ruleset ruleset = scoreInfo.Ruleset.CreateInstance(); + ScoreProcessor scoreProcessor = ruleset.CreateScoreProcessor(); + + int baseScore = scoreInfo.Statistics.Where(kvp => kvp.Key.AffectsAccuracy()) + .Sum(kvp => kvp.Value * scoreProcessor.GetBaseScoreForResult(kvp.Key)); + int maxBaseScore = scoreInfo.MaximumStatistics.Where(kvp => kvp.Key.AffectsAccuracy()) + .Sum(kvp => kvp.Value * scoreProcessor.GetBaseScoreForResult(kvp.Key)); + + return maxBaseScore == 0 ? 1 : baseScore / (double)maxBaseScore; + } + /// /// Used to populate the model using data parsed from its corresponding replay file. /// diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index cd1e81046d..27402d522c 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -11,16 +11,6 @@ namespace osu.Game.Rulesets.Judgements /// public class Judgement { - /// - /// The score awarded for a small bonus. - /// - public const int SMALL_BONUS_SCORE = 10; - - /// - /// The score awarded for a large bonus. - /// - public const int LARGE_BONUS_SCORE = 50; - /// /// The default health increase for a maximum judgement, as a proportion of total health. /// By default, each maximum judgement restores 5% of total health. @@ -91,23 +81,11 @@ namespace osu.Game.Rulesets.Judgements } } - /// - /// The numeric score representation for the maximum achievable result. - /// - public int MaxNumericResult => ToNumericResult(MaxResult); - /// /// The health increase for the maximum achievable result. /// public double MaxHealthIncrease => HealthIncreaseFor(MaxResult); - /// - /// Retrieves the numeric score representation of a . - /// - /// The to find the numeric score representation for. - /// The numeric score representation of . - public int NumericResultFor(JudgementResult result) => ToNumericResult(result.Type); - /// /// Retrieves the numeric health increase of a . /// @@ -165,41 +143,6 @@ namespace osu.Game.Rulesets.Judgements /// The numeric health increase of . public double HealthIncreaseFor(JudgementResult result) => HealthIncreaseFor(result.Type); - public override string ToString() => $"MaxResult:{MaxResult} MaxScore:{MaxNumericResult}"; - - public static int ToNumericResult(HitResult result) - { - switch (result) - { - default: - return 0; - - case HitResult.SmallTickHit: - return 10; - - case HitResult.LargeTickHit: - return 30; - - case HitResult.Meh: - return 50; - - case HitResult.Ok: - return 100; - - case HitResult.Good: - return 200; - - case HitResult.Great: - // Perfect doesn't actually give more score / accuracy directly. - case HitResult.Perfect: - return 300; - - case HitResult.SmallBonus: - return SMALL_BONUS_SCORE; - - case HitResult.LargeBonus: - return LARGE_BONUS_SCORE; - } - } + public override string ToString() => $"MaxResult:{MaxResult}"; } } diff --git a/osu.Game/Rulesets/Judgements/JudgementResult.cs b/osu.Game/Rulesets/Judgements/JudgementResult.cs index db621b4851..1b915d52b7 100644 --- a/osu.Game/Rulesets/Judgements/JudgementResult.cs +++ b/osu.Game/Rulesets/Judgements/JudgementResult.cs @@ -112,6 +112,6 @@ namespace osu.Game.Rulesets.Judgements RawTime = null; } - public override string ToString() => $"{Type} (Score:{Judgement.NumericResultFor(this)} HP:{Judgement.HealthIncreaseFor(this)} {Judgement})"; + public override string ToString() => $"{Type} ({Judgement})"; } } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index aa8905c0b6..5123668e54 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -227,12 +227,12 @@ namespace osu.Game.Rulesets.Scoring if (result.Judgement.MaxResult.AffectsAccuracy()) { - currentMaximumBaseScore += GetMaxNumericResultFor(result); + currentMaximumBaseScore += GetBaseScoreForResult(result.Judgement.MaxResult); currentAccuracyJudgementCount++; } if (result.Type.AffectsAccuracy()) - currentBaseScore += GetNumericResultFor(result); + currentBaseScore += GetBaseScoreForResult(result.Type); if (result.Type.IsBonus()) currentBonusPortion += GetBonusScoreChange(result); @@ -276,12 +276,12 @@ namespace osu.Game.Rulesets.Scoring if (result.Judgement.MaxResult.AffectsAccuracy()) { - currentMaximumBaseScore -= GetMaxNumericResultFor(result); + currentMaximumBaseScore -= GetBaseScoreForResult(result.Judgement.MaxResult); currentAccuracyJudgementCount--; } if (result.Type.AffectsAccuracy()) - currentBaseScore -= GetNumericResultFor(result); + currentBaseScore -= GetBaseScoreForResult(result.Type); if (result.Type.IsBonus()) currentBonusPortion -= GetBonusScoreChange(result); @@ -297,21 +297,51 @@ namespace osu.Game.Rulesets.Scoring updateScore(); } - protected virtual double GetBonusScoreChange(JudgementResult result) => GetNumericResultFor(result); - - protected virtual double GetComboScoreChange(JudgementResult result) => GetMaxNumericResultFor(result) * Math.Pow(result.ComboAfterJudgement, COMBO_EXPONENT); + /// + /// Gets the final score change to be applied to the bonus portion of the score. + /// + /// The judgement result. + protected virtual double GetBonusScoreChange(JudgementResult result) => GetBaseScoreForResult(result.Type); /// - /// Retrieves the numeric score representation for a . + /// Gets the final score change to be applied to the combo portion of the score. /// - /// The . - protected virtual double GetNumericResultFor(JudgementResult result) => result.Judgement.NumericResultFor(result); + /// The judgement result. + protected virtual double GetComboScoreChange(JudgementResult result) => GetBaseScoreForResult(result.Judgement.MaxResult) * Math.Pow(result.ComboAfterJudgement, COMBO_EXPONENT); - /// - /// Retrieves the maximum numeric score representation for a . - /// - /// The . - protected virtual double GetMaxNumericResultFor(JudgementResult result) => result.Judgement.MaxNumericResult; + public virtual int GetBaseScoreForResult(HitResult result) + { + switch (result) + { + default: + return 0; + + case HitResult.SmallTickHit: + return 10; + + case HitResult.LargeTickHit: + return 30; + + case HitResult.Meh: + return 50; + + case HitResult.Ok: + return 100; + + case HitResult.Good: + return 200; + + case HitResult.Great: + case HitResult.Perfect: // Perfect doesn't actually give more score / accuracy directly. + return 300; + + case HitResult.SmallBonus: + return 10; + + case HitResult.LargeBonus: + return 50; + } + } protected virtual void ApplyScoreChange(JudgementResult result) { @@ -540,7 +570,7 @@ namespace osu.Game.Rulesets.Scoring /// /// /// Used to compute accuracy. - /// See: and . + /// See: and . /// [Key(0)] public double BaseScore { get; set; } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index fa930d77d3..dbd9a106df 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -34,9 +34,10 @@ namespace osu.Game.Scoring.Legacy /// 30000005: Introduce combo exponent in the osu! gamemode. Reconvert all scores. /// 30000006: Fix edge cases in conversion after combo exponent introduction that lead to NaNs. Reconvert all scores. /// 30000007: Adjust osu!mania combo and accuracy portions and judgement scoring values. Reconvert all scores. + /// 30000008: Add accuracy conversion. Reconvert all scores. /// /// - public const int LATEST_VERSION = 30000007; + public const int LATEST_VERSION = 30000008; /// /// The first stable-compatible YYYYMMDD format version given to lazer usage of replays. diff --git a/osu.Game/Scoring/ScoreImporter.cs b/osu.Game/Scoring/ScoreImporter.cs index b216c0897e..8e28707107 100644 --- a/osu.Game/Scoring/ScoreImporter.cs +++ b/osu.Game/Scoring/ScoreImporter.cs @@ -17,7 +17,6 @@ using osu.Game.Scoring.Legacy; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using Realms; @@ -107,7 +106,7 @@ namespace osu.Game.Scoring else if (model.IsLegacyScore) { model.LegacyTotalScore = model.TotalScore; - model.TotalScore = StandardisedScoreMigrationTools.ConvertFromLegacyTotalScore(model, beatmaps()); + StandardisedScoreMigrationTools.UpdateFromLegacy(model, beatmaps()); } } @@ -125,13 +124,14 @@ namespace osu.Game.Scoring var beatmap = score.BeatmapInfo!.Detach(); var ruleset = score.Ruleset.Detach(); var rulesetInstance = ruleset.CreateInstance(); + var scoreProcessor = rulesetInstance.CreateScoreProcessor(); Debug.Assert(rulesetInstance != null); // Populate the maximum statistics. HitResult maxBasicResult = rulesetInstance.GetHitResults() .Select(h => h.result) - .Where(h => h.IsBasic()).MaxBy(Judgement.ToNumericResult); + .Where(h => h.IsBasic()).MaxBy(scoreProcessor.GetBaseScoreForResult); foreach ((HitResult result, int count) in score.Statistics) {