mirror of
https://github.com/ppy/osu.git
synced 2025-02-15 07:32:58 +08:00
Merge pull request #24988 from smoogipoo/fix-legacy-score-multipliers-2
Fix legacy score calculators using incorrect mod multipliers
This commit is contained in:
commit
b3d3ae87be
@ -2,10 +2,13 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Catch.Mods;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
@ -137,5 +140,53 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
if (increaseCombo)
|
if (increaseCombo)
|
||||||
combo++;
|
combo++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double GetLegacyScoreMultiplier(IReadOnlyList<Mod> 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ using osu.Game.Rulesets.Objects;
|
|||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps.Patterns;
|
using osu.Game.Rulesets.Mania.Beatmaps.Patterns;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
|
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
|
||||||
|
using osu.Game.Rulesets.Scoring.Legacy;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -43,39 +44,41 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
: base(beatmap, ruleset)
|
: base(beatmap, ruleset)
|
||||||
{
|
{
|
||||||
IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo);
|
IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo);
|
||||||
|
TargetColumns = GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap));
|
||||||
|
|
||||||
double roundedCircleSize = Math.Round(beatmap.Difficulty.CircleSize);
|
if (IsForCurrentRuleset && TargetColumns > ManiaRuleset.MAX_STAGE_KEYS)
|
||||||
double roundedOverallDifficulty = Math.Round(beatmap.Difficulty.OverallDifficulty);
|
|
||||||
|
|
||||||
if (IsForCurrentRuleset)
|
|
||||||
{
|
{
|
||||||
TargetColumns = GetColumnCountForNonConvert(beatmap.BeatmapInfo);
|
TargetColumns /= 2;
|
||||||
|
Dual = true;
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
originalTargetColumns = TargetColumns;
|
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);
|
return (int)Math.Max(1, roundedCircleSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// 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.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Mods;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Scoring.Legacy;
|
using osu.Game.Rulesets.Scoring.Legacy;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Difficulty
|
namespace osu.Game.Rulesets.Mania.Difficulty
|
||||||
@ -12,5 +17,50 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
{
|
{
|
||||||
return new LegacyScoreAttributes { ComboScore = 1000000 };
|
return new LegacyScoreAttributes { ComboScore = 1000000 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double GetLegacyScoreMultiplier(IReadOnlyList<Mod> 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<ManiaKeyMod>().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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
|
|
||||||
public bool Matches(BeatmapInfo beatmapInfo)
|
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)
|
public bool TryParseCustomKeywordCriteria(string key, Operator op, string value)
|
||||||
|
@ -2,11 +2,14 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Scoring.Legacy;
|
using osu.Game.Rulesets.Scoring.Legacy;
|
||||||
@ -174,5 +177,58 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
if (increaseCombo)
|
if (increaseCombo)
|
||||||
combo++;
|
combo++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double GetLegacyScoreMultiplier(IReadOnlyList<Mod> 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,16 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Scoring.Legacy;
|
using osu.Game.Rulesets.Scoring.Legacy;
|
||||||
|
using osu.Game.Rulesets.Taiko.Mods;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Difficulty
|
namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||||
@ -194,5 +197,47 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
if (increaseCombo)
|
if (increaseCombo)
|
||||||
combo++;
|
combo++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double GetLegacyScoreMultiplier(IReadOnlyList<Mod> 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,8 +127,9 @@ namespace osu.Game.Tests.Database
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[TestCase(30000002)]
|
||||||
public void TestScoreUpgradeSuccess()
|
[TestCase(30000003)]
|
||||||
|
public void TestScoreUpgradeSuccess(int scoreVersion)
|
||||||
{
|
{
|
||||||
ScoreInfo scoreInfo = null!;
|
ScoreInfo scoreInfo = null!;
|
||||||
|
|
||||||
@ -138,7 +139,7 @@ namespace osu.Game.Tests.Database
|
|||||||
{
|
{
|
||||||
r.Add(scoreInfo = new ScoreInfo(ruleset: r.All<RulesetInfo>().First(), beatmap: r.All<BeatmapInfo>().First())
|
r.Add(scoreInfo = new ScoreInfo(ruleset: r.All<RulesetInfo>().First(), beatmap: r.All<BeatmapInfo>().First())
|
||||||
{
|
{
|
||||||
TotalScoreVersion = 30000002,
|
TotalScoreVersion = scoreVersion,
|
||||||
LegacyTotalScore = 123456,
|
LegacyTotalScore = 123456,
|
||||||
IsLegacyScore = true,
|
IsLegacyScore = true,
|
||||||
});
|
});
|
||||||
|
@ -235,7 +235,9 @@ namespace osu.Game
|
|||||||
Logger.Log("Querying for scores that need total score conversion...");
|
Logger.Log("Querying for scores that need total score conversion...");
|
||||||
|
|
||||||
HashSet<Guid> scoreIds = realmAccess.Run(r => new HashSet<Guid>(r.All<ScoreInfo>()
|
HashSet<Guid> scoreIds = realmAccess.Run(r => new HashSet<Guid>(r.All<ScoreInfo>()
|
||||||
.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)));
|
.AsEnumerable().Select(s => s.ID)));
|
||||||
|
|
||||||
Logger.Log($"Found {scoreIds.Count} scores which require total score conversion.");
|
Logger.Log($"Found {scoreIds.Count} scores which require total score conversion.");
|
||||||
|
@ -10,7 +10,6 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
using osu.Game.IO.Legacy;
|
using osu.Game.IO.Legacy;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -225,28 +224,30 @@ namespace osu.Game.Database
|
|||||||
ILegacyScoreSimulator sv1Simulator = legacyRuleset.CreateLegacyScoreSimulator();
|
ILegacyScoreSimulator sv1Simulator = legacyRuleset.CreateLegacyScoreSimulator();
|
||||||
LegacyScoreAttributes attributes = sv1Simulator.Simulate(beatmap, playableBeatmap);
|
LegacyScoreAttributes attributes = sv1Simulator.Simulate(beatmap, playableBeatmap);
|
||||||
|
|
||||||
return ConvertFromLegacyTotalScore(score, attributes);
|
return ConvertFromLegacyTotalScore(score, LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap.Beatmap), attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts from <see cref="ScoreInfo.LegacyTotalScore"/> to the new standardised scoring of <see cref="ScoreProcessor"/>.
|
/// Converts from <see cref="ScoreInfo.LegacyTotalScore"/> to the new standardised scoring of <see cref="ScoreProcessor"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="score">The score to convert the total score of.</param>
|
/// <param name="score">The score to convert the total score of.</param>
|
||||||
/// <param name="attributes">Difficulty attributes providing the legacy scoring values
|
/// <param name="difficulty">The beatmap difficulty.</param>
|
||||||
/// (<see cref="DifficultyAttributes.LegacyAccuracyScore"/>, <see cref="DifficultyAttributes.LegacyComboScore"/>, and <see cref="DifficultyAttributes.LegacyBonusScoreRatio"/>)
|
/// <param name="attributes">The legacy scoring attributes for the beatmap which the score was set on.</param>
|
||||||
/// for the beatmap which the score was set on.</param>
|
|
||||||
/// <returns>The standardised total score.</returns>
|
/// <returns>The standardised total score.</returns>
|
||||||
public static long ConvertFromLegacyTotalScore(ScoreInfo score, LegacyScoreAttributes attributes)
|
public static long ConvertFromLegacyTotalScore(ScoreInfo score, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes)
|
||||||
{
|
{
|
||||||
if (!score.IsLegacyScore)
|
if (!score.IsLegacyScore)
|
||||||
return score.TotalScore;
|
return score.TotalScore;
|
||||||
|
|
||||||
Debug.Assert(score.LegacyTotalScore != null);
|
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;
|
int maximumLegacyAccuracyScore = attributes.AccuracyScore;
|
||||||
long maximumLegacyComboScore = (long)Math.Round(attributes.ComboScore * modMultiplier);
|
long maximumLegacyComboScore = (long)Math.Round(attributes.ComboScore * legacyModMultiplier);
|
||||||
double maximumLegacyBonusRatio = attributes.BonusScoreRatio;
|
double maximumLegacyBonusRatio = attributes.BonusScoreRatio;
|
||||||
|
|
||||||
// The part of total score that doesn't include bonus.
|
// 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.
|
// The bonus proportion makes up the rest of the score that exceeds maximumLegacyBaseScore.
|
||||||
double bonusProportion = Math.Max(0, ((long)score.LegacyTotalScore - maximumLegacyBaseScore) * maximumLegacyBonusRatio);
|
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)
|
switch (score.Ruleset.OnlineID)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Scoring.Legacy
|
namespace osu.Game.Rulesets.Scoring.Legacy
|
||||||
{
|
{
|
||||||
@ -16,5 +18,13 @@ namespace osu.Game.Rulesets.Scoring.Legacy
|
|||||||
/// <param name="workingBeatmap">The working beatmap.</param>
|
/// <param name="workingBeatmap">The working beatmap.</param>
|
||||||
/// <param name="playableBeatmap">A playable version of the beatmap for the ruleset.</param>
|
/// <param name="playableBeatmap">A playable version of the beatmap for the ruleset.</param>
|
||||||
LegacyScoreAttributes Simulate(IWorkingBeatmap workingBeatmap, IBeatmap playableBeatmap);
|
LegacyScoreAttributes Simulate(IWorkingBeatmap workingBeatmap, IBeatmap playableBeatmap);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the legacy score multiplier for the mods. This is only used during legacy score conversion.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mods">The mods.</param>
|
||||||
|
/// <param name="difficulty">Extra difficulty parameters.</param>
|
||||||
|
/// <returns>The legacy multiplier.</returns>
|
||||||
|
double GetLegacyScoreMultiplier(IReadOnlyList<Mod> mods, LegacyBeatmapConversionDifficultyInfo difficulty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
// 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.Beatmaps;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Scoring.Legacy
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A set of properties that are required to facilitate beatmap conversion between legacy rulesets.
|
||||||
|
/// </summary>
|
||||||
|
public class LegacyBeatmapConversionDifficultyInfo : IBeatmapDifficultyInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The beatmap's ruleset.
|
||||||
|
/// </summary>
|
||||||
|
public IRulesetInfo SourceRuleset { get; set; } = new RulesetInfo();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The beatmap circle size.
|
||||||
|
/// </summary>
|
||||||
|
public float CircleSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The beatmap overall difficulty.
|
||||||
|
/// </summary>
|
||||||
|
public float OverallDifficulty { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The count of hitcircles in the beatmap.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// When converting from osu! ruleset beatmaps, this is equivalent to the sum of sliders and spinners in the beatmap.
|
||||||
|
/// </remarks>
|
||||||
|
public int CircleCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The total count of hitobjects in the beatmap.
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -30,9 +30,10 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
/// <item><description>30000001: Appends <see cref="LegacyReplaySoloScoreInfo"/> to the end of scores.</description></item>
|
/// <item><description>30000001: Appends <see cref="LegacyReplaySoloScoreInfo"/> to the end of scores.</description></item>
|
||||||
/// <item><description>30000002: Score stored to replay calculated using the Score V2 algorithm. Legacy scores on this version are candidate to Score V1 -> V2 conversion.</description></item>
|
/// <item><description>30000002: Score stored to replay calculated using the Score V2 algorithm. Legacy scores on this version are candidate to Score V1 -> V2 conversion.</description></item>
|
||||||
/// <item><description>30000003: First version after converting legacy total score to standardised.</description></item>
|
/// <item><description>30000003: First version after converting legacy total score to standardised.</description></item>
|
||||||
|
/// <item><description>30000004: Fixed mod multipliers during legacy score conversion. Reconvert all scores.</description></item>
|
||||||
/// </list>
|
/// </list>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public const int LATEST_VERSION = 30000003;
|
public const int LATEST_VERSION = 30000004;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The first stable-compatible YYYYMMDD format version given to lazer usage of replays.
|
/// The first stable-compatible YYYYMMDD format version given to lazer usage of replays.
|
||||||
|
Loading…
Reference in New Issue
Block a user