Skip to content

Fix mod score multiplier rounding to 1.00x with specific mod combinations #26140

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jan 4, 2024
17 changes: 17 additions & 0 deletions osu.Game.Tests/Mods/ModUtilsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,13 @@ public void TestInvalidFreeModScenarios(Mod[] inputMods, Type[] expectedInvalid)
Assert.That(invalid?.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
}

[Test]
public void TestFormatScoreMultiplier()
{
Assert.AreEqual(ModUtils.FormatScoreMultiplier(0.9999).ToString(), "0.99x");
Assert.AreEqual(ModUtils.FormatScoreMultiplier(1.0001).ToString(), "1.01x");
}

public abstract class CustomMod1 : Mod, IModCompatibilitySpecification
{
}
Expand Down Expand Up @@ -339,6 +346,16 @@ private class InvalidMultiplayerFreeMod : Mod
public override bool ValidForMultiplayerAsFreeMod => false;
}

public class EditableMod : Mod
{
public override string Name => string.Empty;
public override LocalisableString Description => string.Empty;
public override string Acronym => string.Empty;
public override double ScoreMultiplier => Multiplier;

public double Multiplier = 1;
}

public interface IModCompatibilitySpecification
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Select;
using osu.Game.Utils;

namespace osu.Game.Tests.Visual.UserInterface
{
Expand Down Expand Up @@ -74,7 +76,7 @@ private void changeMods(IReadOnlyList<Mod> mods)
private bool assertModsMultiplier(IEnumerable<Mod> mods)
{
double multiplier = mods.Aggregate(1.0, (current, mod) => current * mod.ScoreMultiplier);
string expectedValue = multiplier.Equals(1.0) ? string.Empty : $"{multiplier:N2}x";
string expectedValue = multiplier == 1 ? string.Empty : ModUtils.FormatScoreMultiplier(multiplier).ToString();

return expectedValue == footerButtonMods.MultiplierText.Current.Value;
}
Expand Down
4 changes: 2 additions & 2 deletions osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
Expand All @@ -15,6 +14,7 @@
using osu.Game.Graphics.UserInterface;
using osu.Game.Localisation;
using osu.Game.Rulesets.Mods;
using osu.Game.Utils;
using osuTK;

namespace osu.Game.Overlays.Mods
Expand Down Expand Up @@ -147,7 +147,7 @@ private partial class EffectCounter : RollingCounter<double>
{
protected override double RollingDuration => 500;

protected override LocalisableString FormatCount(double count) => count.ToLocalisableString(@"0.00x");
protected override LocalisableString FormatCount(double count) => ModUtils.FormatScoreMultiplier(count);

protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText
{
Expand Down
8 changes: 4 additions & 4 deletions osu.Game/Screens/Select/FooterButtonMods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using osuTK;
using osuTK.Graphics;
using osu.Game.Input.Bindings;
using osu.Game.Utils;

namespace osu.Game.Screens.Select
{
Expand Down Expand Up @@ -87,12 +88,11 @@ protected override void LoadComplete()
private void updateMultiplierText() => Schedule(() =>
{
double multiplier = Current.Value?.Aggregate(1.0, (current, mod) => current * mod.ScoreMultiplier) ?? 1;
MultiplierText.Text = multiplier == 1 ? string.Empty : ModUtils.FormatScoreMultiplier(multiplier);

MultiplierText.Text = multiplier.Equals(1.0) ? string.Empty : $"{multiplier:N2}x";

if (multiplier > 1.0)
if (multiplier > 1)
MultiplierText.FadeColour(highMultiplierColour, 200);
else if (multiplier < 1.0)
else if (multiplier < 1)
MultiplierText.FadeColour(lowMultiplierColour, 200);
else
MultiplierText.FadeColour(Color4.White, 200);
Expand Down
18 changes: 18 additions & 0 deletions osu.Game/Utils/ModUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Localisation;
using osu.Game.Online.API;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
Expand Down Expand Up @@ -226,5 +228,21 @@ public static bool InstantiateValidModsForRuleset(Ruleset ruleset, IEnumerable<A

return proposedWereValid;
}

/// <summary>
/// Given a value of a score multiplier, returns a string version with special handling for a value near 1.00x.
/// </summary>
/// <param name="scoreMultiplier">The value of the score multiplier.</param>
/// <returns>A formatted score multiplier with a trailing "x" symbol</returns>
public static LocalisableString FormatScoreMultiplier(double scoreMultiplier)
{
// Round multiplier values away from 1.00x to two significant digits.
if (scoreMultiplier > 1)
scoreMultiplier = Math.Ceiling(scoreMultiplier * 100) / 100;
else
scoreMultiplier = Math.Floor(scoreMultiplier * 100) / 100;

return scoreMultiplier.ToLocalisableString("0.00x");
}
}
}