From b4b7a7ea5ed60e6da504b7d9df4ec9b054f020dc Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 2 Oct 2023 15:53:05 +0900 Subject: [PATCH 1/3] Add LegacyBeatmapConversionDifficultyInfo abstraction --- .../Beatmaps/ManiaBeatmapConverter.cs | 53 ++++++++------- .../ManiaFilterCriteria.cs | 2 +- .../LegacyBeatmapConversionDifficultyInfo.cs | 67 +++++++++++++++++++ 3 files changed, 96 insertions(+), 26 deletions(-) create mode 100644 osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index bdc5a00583..aaef69f119 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -14,6 +14,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Mania.Beatmaps.Patterns; using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy; +using osu.Game.Rulesets.Scoring.Legacy; using osu.Game.Utils; using osuTK; @@ -43,39 +44,41 @@ namespace osu.Game.Rulesets.Mania.Beatmaps : base(beatmap, ruleset) { IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo); + TargetColumns = GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap)); - double roundedCircleSize = Math.Round(beatmap.Difficulty.CircleSize); - double roundedOverallDifficulty = Math.Round(beatmap.Difficulty.OverallDifficulty); - - if (IsForCurrentRuleset) + if (IsForCurrentRuleset && TargetColumns > ManiaRuleset.MAX_STAGE_KEYS) { - TargetColumns = GetColumnCountForNonConvert(beatmap.BeatmapInfo); - - if (TargetColumns > ManiaRuleset.MAX_STAGE_KEYS) - { - TargetColumns /= 2; - Dual = true; - } - } - else - { - float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasDuration) / beatmap.HitObjects.Count; - if (percentSliderOrSpinner < 0.2) - TargetColumns = 7; - else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5) - TargetColumns = roundedOverallDifficulty > 5 ? 7 : 6; - else if (percentSliderOrSpinner > 0.6) - TargetColumns = roundedOverallDifficulty > 4 ? 5 : 4; - else - TargetColumns = Math.Max(4, Math.Min((int)roundedOverallDifficulty + 1, 7)); + TargetColumns /= 2; + Dual = true; } originalTargetColumns = TargetColumns; } - public static int GetColumnCountForNonConvert(BeatmapInfo beatmapInfo) + public static int GetColumnCount(LegacyBeatmapConversionDifficultyInfo difficulty) { - double roundedCircleSize = Math.Round(beatmapInfo.Difficulty.CircleSize); + if (new ManiaRuleset().RulesetInfo.Equals(difficulty.SourceRuleset)) + return GetColumnCountForNonConvert(difficulty); + + double roundedCircleSize = Math.Round(difficulty.CircleSize); + double roundedOverallDifficulty = Math.Round(difficulty.OverallDifficulty); + + int countSliderOrSpinner = difficulty.TotalObjectCount - difficulty.CircleCount; + float percentSpecialObjects = (float)countSliderOrSpinner / difficulty.TotalObjectCount; + + if (percentSpecialObjects < 0.2) + return 7; + if (percentSpecialObjects < 0.3 || roundedCircleSize >= 5) + return roundedOverallDifficulty > 5 ? 7 : 6; + if (percentSpecialObjects > 0.6) + return roundedOverallDifficulty > 4 ? 5 : 4; + + return Math.Max(4, Math.Min((int)roundedOverallDifficulty + 1, 7)); + } + + public static int GetColumnCountForNonConvert(IBeatmapDifficultyInfo difficulty) + { + double roundedCircleSize = Math.Round(difficulty.CircleSize); return (int)Math.Max(1, roundedCircleSize); } diff --git a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs index c8832dfdfb..7f8a00bf88 100644 --- a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs +++ b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania public bool Matches(BeatmapInfo beatmapInfo) { - return !keys.HasFilter || (beatmapInfo.Ruleset.OnlineID == new ManiaRuleset().LegacyID && keys.IsInRange(ManiaBeatmapConverter.GetColumnCountForNonConvert(beatmapInfo))); + return !keys.HasFilter || (beatmapInfo.Ruleset.OnlineID == new ManiaRuleset().LegacyID && keys.IsInRange(ManiaBeatmapConverter.GetColumnCountForNonConvert(beatmapInfo.Difficulty))); } public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs new file mode 100644 index 0000000000..97ccf787af --- /dev/null +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs @@ -0,0 +1,67 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Scoring.Legacy +{ + /// + /// A set of properties that are required to facilitate beatmap conversion between legacy rulesets. + /// + public class LegacyBeatmapConversionDifficultyInfo : IBeatmapDifficultyInfo + { + /// + /// The beatmap's ruleset. + /// + public IRulesetInfo SourceRuleset { get; set; } = new RulesetInfo(); + + /// + /// The beatmap circle size. + /// + public float CircleSize { get; set; } + + /// + /// The beatmap overall difficulty. + /// + public float OverallDifficulty { get; set; } + + /// + /// The count of hitcircles in the beatmap. + /// + /// + /// When converting from osu! ruleset beatmaps, this is equivalent to the sum of sliders and spinners in the beatmap. + /// + public int CircleCount { get; set; } + + /// + /// The total count of hitobjects in the beatmap. + /// + public int TotalObjectCount { get; set; } + + float IBeatmapDifficultyInfo.DrainRate => 0; + float IBeatmapDifficultyInfo.ApproachRate => 0; + double IBeatmapDifficultyInfo.SliderMultiplier => 0; + double IBeatmapDifficultyInfo.SliderTickRate => 0; + + public static LegacyBeatmapConversionDifficultyInfo FromAPIBeatmap(APIBeatmap apiBeatmap) => new LegacyBeatmapConversionDifficultyInfo + { + SourceRuleset = apiBeatmap.Ruleset, + CircleSize = apiBeatmap.CircleSize, + OverallDifficulty = apiBeatmap.OverallDifficulty, + CircleCount = apiBeatmap.CircleCount, + TotalObjectCount = apiBeatmap.SliderCount + apiBeatmap.SpinnerCount + apiBeatmap.CircleCount + }; + + public static LegacyBeatmapConversionDifficultyInfo FromBeatmap(IBeatmap beatmap) => new LegacyBeatmapConversionDifficultyInfo + { + SourceRuleset = beatmap.BeatmapInfo.Ruleset, + CircleSize = beatmap.Difficulty.CircleSize, + OverallDifficulty = beatmap.Difficulty.OverallDifficulty, + CircleCount = beatmap.HitObjects.Count(h => h is not IHasDuration), + TotalObjectCount = beatmap.HitObjects.Count + }; + } +} From da2a4681d9201eadb3ad0e6cd1de486042437d3d Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 2 Oct 2023 15:58:31 +0900 Subject: [PATCH 2/3] Add method to retrieve legacy score multiplier --- .../Difficulty/CatchLegacyScoreSimulator.cs | 51 +++++++++++++++++ .../Difficulty/ManiaLegacyScoreSimulator.cs | 50 +++++++++++++++++ .../Difficulty/OsuLegacyScoreSimulator.cs | 56 +++++++++++++++++++ .../Difficulty/TaikoLegacyScoreSimulator.cs | 45 +++++++++++++++ .../Scoring/Legacy/ILegacyScoreSimulator.cs | 10 ++++ 5 files changed, 212 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs index 0183d723b2..746f5713e4 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs @@ -2,10 +2,13 @@ // 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.Rulesets.Catch.Mods; using osu.Game.Rulesets.Catch.Objects; 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.Scoring; @@ -137,5 +140,53 @@ namespace osu.Game.Rulesets.Catch.Difficulty if (increaseCombo) combo++; } + + public double GetLegacyScoreMultiplier(IReadOnlyList mods, LegacyBeatmapConversionDifficultyInfo difficulty) + { + bool scoreV2 = mods.Any(m => m is ModScoreV2); + + double multiplier = 1.0; + + foreach (var mod in mods) + { + switch (mod) + { + case CatchModNoFail: + multiplier *= scoreV2 ? 1.0 : 0.5; + break; + + case CatchModEasy: + multiplier *= 0.5; + break; + + case CatchModHalfTime: + case CatchModDaycore: + multiplier *= 0.3; + break; + + case CatchModHidden: + multiplier *= scoreV2 ? 1.0 : 1.06; + break; + + case CatchModHardRock: + multiplier *= 1.12; + break; + + case CatchModDoubleTime: + case CatchModNightcore: + multiplier *= 1.06; + break; + + case CatchModFlashlight: + multiplier *= 1.12; + break; + + case CatchModRelax: + return 0; + } + } + + return multiplier; + } } } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs index 098ccdf21d..ddb4b868a3 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs @@ -1,7 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Mods; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring.Legacy; namespace osu.Game.Rulesets.Mania.Difficulty @@ -12,5 +17,50 @@ namespace osu.Game.Rulesets.Mania.Difficulty { return new LegacyScoreAttributes { ComboScore = 1000000 }; } + + public double GetLegacyScoreMultiplier(IReadOnlyList mods, LegacyBeatmapConversionDifficultyInfo difficulty) + { + bool scoreV2 = mods.Any(m => m is ModScoreV2); + + double multiplier = 1.0; + + foreach (var mod in mods) + { + switch (mod) + { + case ManiaModNoFail: + multiplier *= scoreV2 ? 1.0 : 0.5; + break; + + case ManiaModEasy: + multiplier *= 0.5; + break; + + case ManiaModHalfTime: + case ManiaModDaycore: + multiplier *= 0.5; + break; + } + } + + if (new ManiaRuleset().RulesetInfo.Equals(difficulty.SourceRuleset)) + return multiplier; + + // Apply key mod multipliers. + + int originalColumns = ManiaBeatmapConverter.GetColumnCount(difficulty); + int actualColumns = originalColumns; + + actualColumns = mods.OfType().SingleOrDefault()?.KeyCount ?? actualColumns; + if (mods.Any(m => m is ManiaModDualStages)) + actualColumns *= 2; + + if (actualColumns > originalColumns) + multiplier *= 0.9; + else if (actualColumns < originalColumns) + multiplier *= 0.9 - 0.04 * (originalColumns - actualColumns); + + return multiplier; + } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs index 952ffa5e7a..3a905d77b1 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs @@ -2,11 +2,14 @@ // 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.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.Scoring; using osu.Game.Rulesets.Scoring.Legacy; @@ -174,5 +177,58 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (increaseCombo) combo++; } + + public double GetLegacyScoreMultiplier(IReadOnlyList mods, LegacyBeatmapConversionDifficultyInfo difficulty) + { + bool scoreV2 = mods.Any(m => m is ModScoreV2); + + double multiplier = 1.0; + + foreach (var mod in mods) + { + switch (mod) + { + case OsuModNoFail: + multiplier *= scoreV2 ? 1.0 : 0.5; + break; + + case OsuModEasy: + multiplier *= 0.5; + break; + + case OsuModHalfTime: + case OsuModDaycore: + multiplier *= 0.3; + break; + + case OsuModHidden: + multiplier *= 1.06; + break; + + case OsuModHardRock: + multiplier *= scoreV2 ? 1.10 : 1.06; + break; + + case OsuModDoubleTime: + case OsuModNightcore: + multiplier *= scoreV2 ? 1.20 : 1.12; + break; + + case OsuModFlashlight: + multiplier *= 1.12; + break; + + case OsuModSpunOut: + multiplier *= 0.9; + break; + + case OsuModRelax: + case OsuModAutopilot: + return 0; + } + } + + return multiplier; + } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs index d4538d3c00..6a3eb68a22 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs @@ -2,13 +2,16 @@ // 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.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring.Legacy; +using osu.Game.Rulesets.Taiko.Mods; using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty @@ -194,5 +197,47 @@ namespace osu.Game.Rulesets.Taiko.Difficulty if (increaseCombo) combo++; } + + public double GetLegacyScoreMultiplier(IReadOnlyList mods, LegacyBeatmapConversionDifficultyInfo difficulty) + { + bool scoreV2 = mods.Any(m => m is ModScoreV2); + + double multiplier = 1.0; + + foreach (var mod in mods) + { + switch (mod) + { + case TaikoModNoFail: + multiplier *= scoreV2 ? 1.0 : 0.5; + break; + + case TaikoModEasy: + multiplier *= 0.5; + break; + + case TaikoModHalfTime: + case TaikoModDaycore: + multiplier *= 0.3; + break; + + case TaikoModHidden: + case TaikoModHardRock: + multiplier *= 1.06; + break; + + case TaikoModDoubleTime: + case TaikoModNightcore: + case TaikoModFlashlight: + multiplier *= 1.12; + break; + + case TaikoModRelax: + return 0; + } + } + + return multiplier; + } } } diff --git a/osu.Game/Rulesets/Scoring/Legacy/ILegacyScoreSimulator.cs b/osu.Game/Rulesets/Scoring/Legacy/ILegacyScoreSimulator.cs index fe7843a682..824f38fe2c 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/ILegacyScoreSimulator.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/ILegacyScoreSimulator.cs @@ -1,7 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Scoring.Legacy { @@ -16,5 +18,13 @@ namespace osu.Game.Rulesets.Scoring.Legacy /// The working beatmap. /// A playable version of the beatmap for the ruleset. LegacyScoreAttributes Simulate(IWorkingBeatmap workingBeatmap, IBeatmap playableBeatmap); + + /// + /// Returns the legacy score multiplier for the mods. This is only used during legacy score conversion. + /// + /// The mods. + /// Extra difficulty parameters. + /// The legacy multiplier. + double GetLegacyScoreMultiplier(IReadOnlyList mods, LegacyBeatmapConversionDifficultyInfo difficulty); } } From b9ab4a2b7cf1306f761bc19b1636581211dae9ad Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 2 Oct 2023 16:54:46 +0900 Subject: [PATCH 3/3] Update score conversion to consider legacy multiplier --- .../BackgroundDataStoreProcessorTests.cs | 7 ++++--- osu.Game/BackgroundDataStoreProcessor.cs | 4 +++- .../StandardisedScoreMigrationTools.cs | 19 +++++++++++-------- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 3 ++- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs index da46392e4b..e65088ca2e 100644 --- a/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs +++ b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs @@ -127,8 +127,9 @@ namespace osu.Game.Tests.Database }); } - [Test] - public void TestScoreUpgradeSuccess() + [TestCase(30000002)] + [TestCase(30000003)] + public void TestScoreUpgradeSuccess(int scoreVersion) { ScoreInfo scoreInfo = null!; @@ -138,7 +139,7 @@ namespace osu.Game.Tests.Database { r.Add(scoreInfo = new ScoreInfo(ruleset: r.All().First(), beatmap: r.All().First()) { - TotalScoreVersion = 30000002, + TotalScoreVersion = scoreVersion, LegacyTotalScore = 123456, IsLegacyScore = true, }); diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index f29b100ee8..90e55dea6d 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -235,7 +235,9 @@ namespace osu.Game Logger.Log("Querying for scores that need total score conversion..."); HashSet scoreIds = realmAccess.Run(r => new HashSet(r.All() - .Where(s => !s.BackgroundReprocessingFailed && s.BeatmapInfo != null && s.TotalScoreVersion == 30000002) + .Where(s => !s.BackgroundReprocessingFailed && s.BeatmapInfo != null + && (s.TotalScoreVersion == 30000002 + || s.TotalScoreVersion == 30000003)) .AsEnumerable().Select(s => s.ID))); Logger.Log($"Found {scoreIds.Count} scores which require total score conversion."); diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index dd7d7c7b1b..3d48a5d233 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -10,7 +10,6 @@ using osu.Game.Beatmaps; using osu.Game.Extensions; using osu.Game.IO.Legacy; using osu.Game.Rulesets; -using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; @@ -225,28 +224,30 @@ namespace osu.Game.Database ILegacyScoreSimulator sv1Simulator = legacyRuleset.CreateLegacyScoreSimulator(); LegacyScoreAttributes attributes = sv1Simulator.Simulate(beatmap, playableBeatmap); - return ConvertFromLegacyTotalScore(score, attributes); + return ConvertFromLegacyTotalScore(score, LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap.Beatmap), attributes); } /// /// Converts from to the new standardised scoring of . /// /// The score to convert the total score of. - /// Difficulty attributes providing the legacy scoring values - /// (, , and ) - /// for the beatmap which the score was set on. + /// 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, LegacyScoreAttributes attributes) + public static long ConvertFromLegacyTotalScore(ScoreInfo score, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes) { if (!score.IsLegacyScore) return score.TotalScore; Debug.Assert(score.LegacyTotalScore != null); - double modMultiplier = score.Mods.Select(m => m.ScoreMultiplier).Aggregate(1.0, (c, n) => c * n); + Ruleset ruleset = score.Ruleset.CreateInstance(); + if (ruleset is not ILegacyRuleset legacyRuleset) + return score.TotalScore; + double legacyModMultiplier = legacyRuleset.CreateLegacyScoreSimulator().GetLegacyScoreMultiplier(score.Mods, difficulty); int maximumLegacyAccuracyScore = attributes.AccuracyScore; - long maximumLegacyComboScore = (long)Math.Round(attributes.ComboScore * modMultiplier); + long maximumLegacyComboScore = (long)Math.Round(attributes.ComboScore * legacyModMultiplier); double maximumLegacyBonusRatio = attributes.BonusScoreRatio; // The part of total score that doesn't include bonus. @@ -258,6 +259,8 @@ namespace osu.Game.Database // The bonus proportion makes up the rest of the score that exceeds maximumLegacyBaseScore. double bonusProportion = Math.Max(0, ((long)score.LegacyTotalScore - maximumLegacyBaseScore) * maximumLegacyBonusRatio); + double modMultiplier = score.Mods.Select(m => m.ScoreMultiplier).Aggregate(1.0, (c, n) => c * n); + switch (score.Ruleset.OnlineID) { case 0: diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index 6868c89d26..a95964ac52 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -30,9 +30,10 @@ namespace osu.Game.Scoring.Legacy /// 30000001: Appends to the end of scores. /// 30000002: Score stored to replay calculated using the Score V2 algorithm. Legacy scores on this version are candidate to Score V1 -> V2 conversion. /// 30000003: First version after converting legacy total score to standardised. + /// 30000004: Fixed mod multipliers during legacy score conversion. Reconvert all scores. /// /// - public const int LATEST_VERSION = 30000003; + public const int LATEST_VERSION = 30000004; /// /// The first stable-compatible YYYYMMDD format version given to lazer usage of replays.