Skip to content

Commit

Permalink
Fix calculation of mod multiplier in legacy score simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
smoogipoo committed Aug 28, 2023
1 parent d70df88 commit d23ff5b
Show file tree
Hide file tree
Showing 12 changed files with 386 additions and 33 deletions.
54 changes: 54 additions & 0 deletions osu.Game.Rulesets.Catch/CatchRuleset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
Expand Down Expand Up @@ -215,6 +216,59 @@ public override LocalisableString GetDisplayNameForHitResult(HitResult result)

public ILegacyScoreSimulator CreateLegacyScoreSimulator() => new CatchLegacyScoreSimulator();

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 *= scoreV2 ? 1.0 : 1.12;
break;

case CatchModDoubleTime:
case CatchModNightcore:
multiplier *= scoreV2 ? 1.0 : 1.06;
break;

case CatchModFlashlight:
multiplier *= 1.12;
break;

// case CatchModSpunOut:
// multiplier *= 0.9;
// break;

// case CatchModAutopilot:
case CatchModRelax:
return 0;
}
}

return multiplier;
}

public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();

public override HitObjectComposer CreateHitObjectComposer() => new CatchHitObjectComposer(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,14 @@ public void Simulate(IWorkingBeatmap workingBeatmap, IBeatmap playableBeatmap, I
+ baseBeatmap.Difficulty.CircleSize
+ Math.Clamp((float)objectCount / drainLength * 8, 0, 16)) / 38 * 5);

scoreMultiplier = difficultyPeppyStars * mods.Aggregate(1.0, (current, mod) => current * mod.ScoreMultiplier);
scoreMultiplier = difficultyPeppyStars * new CatchRuleset().GetLegacyScoreMultiplier(mods, new LegacyBeatmapConversionDifficultyInfo
{
IsForTargetRuleset = baseBeatmap.BeatmapInfo.Ruleset.OnlineID == 2,
CircleSize = baseBeatmap.Difficulty.CircleSize,
OverallDifficulty = baseBeatmap.Difficulty.OverallDifficulty,
CircleCount = countNormal,
TotalObjectCount = objectCount
});

foreach (var obj in playableBeatmap.HitObjects)
simulateHit(obj);
Expand Down
51 changes: 27 additions & 24 deletions osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,38 +44,41 @@ public ManiaBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
{
IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo);

double roundedCircleSize = Math.Round(beatmap.Difficulty.CircleSize);
double roundedOverallDifficulty = Math.Round(beatmap.Difficulty.OverallDifficulty);
TargetColumns = GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap));

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)
{
if (difficulty.IsForTargetRuleset)
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(beatmapInfo.Difficulty.CircleSize);
double roundedCircleSize = Math.Round(difficulty.CircleSize);
return (int)Math.Max(1, roundedCircleSize);
}

Expand Down
41 changes: 36 additions & 5 deletions osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// 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.Mods;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;

namespace osu.Game.Rulesets.Mania.Difficulty
Expand All @@ -18,9 +18,40 @@ internal class ManiaLegacyScoreSimulator : ILegacyScoreSimulator

public void Simulate(IWorkingBeatmap workingBeatmap, IBeatmap playableBeatmap, IReadOnlyList<Mod> mods)
{
double multiplier = mods.Where(m => m is not (ModHidden or ModHardRock or ModDoubleTime or ModFlashlight or ManiaModFadeIn))
.Select(m => m.ScoreMultiplier)
.Aggregate(1.0, (c, n) => c * n);
IBeatmap baseBeatmap = workingBeatmap.Beatmap;

int countNormal = 0;
int countSlider = 0;
int countSpinner = 0;

foreach (HitObject obj in baseBeatmap.HitObjects)
{
switch (obj)
{
case IHasPath:
countSlider++;
break;

case IHasDuration:
countSpinner++;
break;

default:
countNormal++;
break;
}
}

int objectCount = countNormal + countSlider + countSpinner;

double multiplier = new ManiaRuleset().GetLegacyScoreMultiplier(mods, new LegacyBeatmapConversionDifficultyInfo
{
IsForTargetRuleset = baseBeatmap.BeatmapInfo.Ruleset.OnlineID == 3,
CircleSize = baseBeatmap.Difficulty.CircleSize,
OverallDifficulty = baseBeatmap.Difficulty.OverallDifficulty,
CircleCount = countNormal,
TotalObjectCount = objectCount
});

ComboScore = (int)(1000000 * multiplier);
}
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class ManiaFilterCriteria : IRulesetFilterCriteria

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)
Expand Down
51 changes: 51 additions & 0 deletions osu.Game.Rulesets.Mania/ManiaRuleset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,57 @@ public override IEnumerable<Mod> GetModsFor(ModType type)

public ILegacyScoreSimulator CreateLegacyScoreSimulator() => new ManiaLegacyScoreSimulator();

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;

// case ManiaModSpunOut:

Check failure on line 339 in osu.Game.Rulesets.Mania/ManiaRuleset.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Fix formatting

Check failure on line 339 in osu.Game.Rulesets.Mania/ManiaRuleset.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Fix formatting
// case ManiaModRelax:

Check failure on line 340 in osu.Game.Rulesets.Mania/ManiaRuleset.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Fix formatting

Check failure on line 340 in osu.Game.Rulesets.Mania/ManiaRuleset.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Fix formatting
// case ManiaModAutopilot:

Check failure on line 341 in osu.Game.Rulesets.Mania/ManiaRuleset.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Fix formatting

Check failure on line 341 in osu.Game.Rulesets.Mania/ManiaRuleset.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Fix formatting
// multiplier *= 0;

Check failure on line 342 in osu.Game.Rulesets.Mania/ManiaRuleset.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Fix formatting

Check failure on line 342 in osu.Game.Rulesets.Mania/ManiaRuleset.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Fix formatting
// break;

Check failure on line 343 in osu.Game.Rulesets.Mania/ManiaRuleset.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Fix formatting

Check failure on line 343 in osu.Game.Rulesets.Mania/ManiaRuleset.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Fix formatting
}
}

if (difficulty.IsForTargetRuleset)
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;
}

public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();

public override IRulesetConfigManager CreateConfig(SettingsStore? settings) => new ManiaRulesetConfigManager(settings, RulesetInfo);
Expand Down
9 changes: 8 additions & 1 deletion osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,14 @@ public void Simulate(IWorkingBeatmap workingBeatmap, IBeatmap playableBeatmap, I
+ baseBeatmap.Difficulty.CircleSize
+ Math.Clamp((float)objectCount / drainLength * 8, 0, 16)) / 38 * 5);

scoreMultiplier = difficultyPeppyStars * mods.Aggregate(1.0, (current, mod) => current * mod.ScoreMultiplier);
scoreMultiplier = difficultyPeppyStars * new OsuRuleset().GetLegacyScoreMultiplier(mods, new LegacyBeatmapConversionDifficultyInfo
{
IsForTargetRuleset = baseBeatmap.BeatmapInfo.Ruleset.OnlineID == 0,
CircleSize = baseBeatmap.Difficulty.CircleSize,
OverallDifficulty = baseBeatmap.Difficulty.OverallDifficulty,
CircleCount = countNormal,
TotalObjectCount = objectCount
});

foreach (var obj in playableBeatmap.HitObjects)
simulateHit(obj);
Expand Down
53 changes: 53 additions & 0 deletions osu.Game.Rulesets.Osu/OsuRuleset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,59 @@ public override IEnumerable<Mod> GetModsFor(ModType type)

public ILegacyScoreSimulator CreateLegacyScoreSimulator() => new OsuLegacyScoreSimulator();

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;
}

public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame();

public override IRulesetConfigManager CreateConfig(SettingsStore? settings) => new OsuRulesetConfigManager(settings, RulesetInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,14 @@ public void Simulate(IWorkingBeatmap workingBeatmap, IBeatmap playableBeatmap, I
+ baseBeatmap.Difficulty.CircleSize
+ Math.Clamp((float)objectCount / drainLength * 8, 0, 16)) / 38 * 5);

modMultiplier = mods.Aggregate(1.0, (current, mod) => current * mod.ScoreMultiplier);
modMultiplier = new TaikoRuleset().GetLegacyScoreMultiplier(mods, new LegacyBeatmapConversionDifficultyInfo
{
IsForTargetRuleset = baseBeatmap.BeatmapInfo.Ruleset.OnlineID == 1,
CircleSize = baseBeatmap.Difficulty.CircleSize,
OverallDifficulty = baseBeatmap.Difficulty.OverallDifficulty,
CircleCount = countNormal,
TotalObjectCount = objectCount
});

foreach (var obj in playableBeatmap.HitObjects)
simulateHit(obj);
Expand Down
Loading

0 comments on commit d23ff5b

Please sign in to comment.