mirror of
https://github.com/ppy/osu.git
synced 2026-06-11 08:03:39 +08:00
Compare commits
7 Commits
@@ -2,12 +2,8 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Mods;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Tests.Rulesets;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
@@ -114,7 +110,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
#region Conversion
|
||||
|
||||
[new Mod[] { new CatchModDifficultyAdjust() }, 0.5],
|
||||
[new Mod[] { new CatchModClassic() }, 1],
|
||||
[new Mod[] { new CatchModClassic() }, 0.96],
|
||||
[new Mod[] { new CatchModMirror() }, 1],
|
||||
|
||||
#endregion
|
||||
@@ -155,17 +151,5 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
[TestCaseSource(nameof(test_cases))]
|
||||
public void TestMultipliers(Mod[] mods, double expectedMultiplier)
|
||||
=> TestModCombination(mods, expectedMultiplier);
|
||||
|
||||
[TestCase(30000001, 0.96)]
|
||||
[TestCase(30000009, 0.96)]
|
||||
[TestCase(30000016, 0.96)]
|
||||
[TestCase(30000017, 1)]
|
||||
[TestCase(null, 1)]
|
||||
public void TestClassicMultiplierVersioning(int? totalScoreVersion, double expectedMultiplier)
|
||||
{
|
||||
var scoreInfo = totalScoreVersion != null ? new ScoreInfo { TotalScoreVersion = totalScoreVersion.Value } : null;
|
||||
var calculator = Ruleset.CreateScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty(), scoreInfo));
|
||||
Assert.That(calculator.CalculateFor([new CatchModClassic()]), Is.EqualTo(expectedMultiplier).Within(Precision.DOUBLE_EPSILON));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using osu.Game.Rulesets.Catch.Mods;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Scoring
|
||||
{
|
||||
@@ -38,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Scoring
|
||||
#region Conversion
|
||||
|
||||
Single<CatchModDifficultyAdjust>(hasMultiplier: 0.5);
|
||||
Single<CatchModClassic>(hasMultiplier: _ => classicMultiplier(context.Score));
|
||||
Single<CatchModClassic>(hasMultiplier: 0.96);
|
||||
// Mirror
|
||||
|
||||
#endregion
|
||||
@@ -83,13 +82,5 @@ namespace osu.Game.Rulesets.Catch.Scoring
|
||||
else
|
||||
return 0.6 + value;
|
||||
}
|
||||
|
||||
private static double classicMultiplier(ScoreInfo? score)
|
||||
{
|
||||
if (score != null && score.TotalScoreVersion < 30000017)
|
||||
return 0.96;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
[new Mod[] { new ManiaModDualStages() }, 1],
|
||||
[new Mod[] { new ManiaModMirror() }, 1],
|
||||
[new Mod[] { new ManiaModDifficultyAdjust() }, 0.5],
|
||||
[new Mod[] { new ManiaModClassic() }, 1],
|
||||
[new Mod[] { new ManiaModClassic() }, 0.96],
|
||||
[new Mod[] { new ManiaModInvert() }, 1],
|
||||
[new Mod[] { new ManiaModConstantSpeed() }, 0.9],
|
||||
[new Mod[] { new ManiaModHoldOff() }, 0.9],
|
||||
@@ -220,17 +220,5 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
}));
|
||||
Assert.That(calculator.CalculateFor([new ManiaModKey4()]), Is.EqualTo(expectedMultiplier).Within(Precision.DOUBLE_EPSILON));
|
||||
}
|
||||
|
||||
[TestCase(30000001, 0.96)]
|
||||
[TestCase(30000009, 0.96)]
|
||||
[TestCase(30000016, 0.96)]
|
||||
[TestCase(30000017, 1)]
|
||||
[TestCase(null, 1)]
|
||||
public void TestClassicMultiplierVersioning(int? totalScoreVersion, double expectedMultiplier)
|
||||
{
|
||||
var scoreInfo = totalScoreVersion != null ? new ScoreInfo { TotalScoreVersion = totalScoreVersion.Value } : null;
|
||||
var calculator = Ruleset.CreateScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty(), scoreInfo));
|
||||
Assert.That(calculator.CalculateFor([new ManiaModClassic()]), Is.EqualTo(expectedMultiplier).Within(Precision.DOUBLE_EPSILON));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
// Dual Stages
|
||||
// Mirror
|
||||
Single<ManiaModDifficultyAdjust>(hasMultiplier: 0.5);
|
||||
Single<ManiaModClassic>(hasMultiplier: _ => classicMultiplier(Context.Score));
|
||||
Single<ManiaModClassic>(hasMultiplier: 0.96);
|
||||
// Invert
|
||||
Single<ManiaModConstantSpeed>(hasMultiplier: 0.9);
|
||||
Single<ManiaModHoldOff>(hasMultiplier: 0.9);
|
||||
@@ -142,13 +142,5 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
|
||||
return new_key_mod_multiplier;
|
||||
}
|
||||
|
||||
private static double classicMultiplier(ScoreInfo? score)
|
||||
{
|
||||
if (score != null && score.TotalScoreVersion < 30000017)
|
||||
return 0.96;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
{
|
||||
public partial class TestSceneOsuModDifficultyAdjust : OsuModTestScene
|
||||
{
|
||||
protected override bool AllowFail => true;
|
||||
|
||||
[Test]
|
||||
public void TestNoAdjustment() => CreateModTest(new ModTestData
|
||||
{
|
||||
@@ -74,88 +72,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
PassCondition = () => checkSomeHit() && checkObjectsPreempt(450)
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestScoreMultiplierCorrectWithNoAdjustment() => CreateModTest(new ModTestData
|
||||
{
|
||||
Mod = new OsuModDifficultyAdjust(),
|
||||
CreateBeatmap = () => new Beatmap
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
Difficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 8
|
||||
}
|
||||
},
|
||||
HitObjects = new List<HitObject>
|
||||
{
|
||||
new HitCircle { StartTime = 1000 },
|
||||
new HitCircle { StartTime = 2000 }
|
||||
}
|
||||
},
|
||||
Autoplay = true,
|
||||
PassCondition = () => Player.ScoreProcessor.TotalScore.Value == 1_000_000,
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestScoreMultiplierCorrectWithSingleAdjustment() => CreateModTest(new ModTestData
|
||||
{
|
||||
Mod = new OsuModDifficultyAdjust
|
||||
{
|
||||
ApproachRate = { Value = 7.3f }
|
||||
},
|
||||
CreateBeatmap = () => new Beatmap
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
Difficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 8,
|
||||
ApproachRate = 7,
|
||||
OverallDifficulty = 6,
|
||||
DrainRate = 5,
|
||||
}
|
||||
},
|
||||
HitObjects = new List<HitObject>
|
||||
{
|
||||
new HitCircle { StartTime = 1000 },
|
||||
new HitCircle { StartTime = 2000 }
|
||||
}
|
||||
},
|
||||
Autoplay = true,
|
||||
PassCondition = () => Player.ScoreProcessor.TotalScore.Value == 850_000,
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestScoreMultiplierCorrectWithMultipleAdjustments() => CreateModTest(new ModTestData
|
||||
{
|
||||
Mod = new OsuModDifficultyAdjust
|
||||
{
|
||||
ApproachRate = { Value = 6.8f },
|
||||
OverallDifficulty = { Value = 6.6f }
|
||||
},
|
||||
CreateBeatmap = () => new Beatmap
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
Difficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 8,
|
||||
ApproachRate = 7,
|
||||
OverallDifficulty = 6,
|
||||
DrainRate = 5,
|
||||
}
|
||||
},
|
||||
HitObjects = new List<HitObject>
|
||||
{
|
||||
new HitCircle { StartTime = 1000 },
|
||||
new HitCircle { StartTime = 2000 }
|
||||
}
|
||||
},
|
||||
Autoplay = true,
|
||||
PassCondition = () => Player.ScoreProcessor.TotalScore.Value == 630_000,
|
||||
});
|
||||
|
||||
private bool checkObjectsPreempt(double target)
|
||||
{
|
||||
var objects = Player.ChildrenOfType<DrawableHitCircle>();
|
||||
|
||||
@@ -2,12 +2,8 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Tests.Rulesets;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
@@ -23,106 +19,94 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[
|
||||
#region Difficulty Reduction
|
||||
|
||||
[new Mod[] { new OsuModEasy() }, 0.8],
|
||||
[new Mod[] { new OsuModEasy { Retries = { Value = 1 } } }, 0.8],
|
||||
[new Mod[] { new OsuModEasy { Retries = { Value = 3 } } }, 0.7],
|
||||
[new Mod[] { new OsuModEasy { Retries = { Value = 5 } } }, 0.5],
|
||||
[new Mod[] { new OsuModEasy { Retries = { Value = 8 } } }, 0.4],
|
||||
|
||||
[new Mod[] { new OsuModEasy() }, 0.5],
|
||||
[new Mod[] { new OsuModNoFail() }, 0.5],
|
||||
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.50 } } }, 0.20],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.55 } } }, 0.27],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.60 } } }, 0.34],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.65 } } }, 0.41],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.70 } } }, 0.48],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.75 } } }, 0.55],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.80 } } }, 0.62],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.85 } } }, 0.69],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.90 } } }, 0.76],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.95 } } }, 0.83],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.99 } } }, 0.83],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.50 } } }, 0.1],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.55 } } }, 0.1],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.60 } } }, 0.2],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.65 } } }, 0.2],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.70 } } }, 0.3],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.75 } } }, 0.3],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.80 } } }, 0.4],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.85 } } }, 0.4],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.90 } } }, 0.5],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.95 } } }, 0.5],
|
||||
[new Mod[] { new OsuModHalfTime { SpeedChange = { Value = 0.99 } } }, 0.5],
|
||||
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.50 } } }, 0.20],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.55 } } }, 0.27],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.60 } } }, 0.34],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.65 } } }, 0.41],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.70 } } }, 0.48],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.75 } } }, 0.55],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.80 } } }, 0.62],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.85 } } }, 0.69],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.90 } } }, 0.76],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.95 } } }, 0.83],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.99 } } }, 0.83],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.50 } } }, 0.1],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.55 } } }, 0.1],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.60 } } }, 0.2],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.65 } } }, 0.2],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.70 } } }, 0.3],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.75 } } }, 0.3],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.80 } } }, 0.4],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.85 } } }, 0.4],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.90 } } }, 0.5],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.95 } } }, 0.5],
|
||||
[new Mod[] { new OsuModDaycore { SpeedChange = { Value = 0.99 } } }, 0.5],
|
||||
|
||||
#endregion
|
||||
|
||||
#region Difficulty Increase
|
||||
|
||||
[new Mod[] { new OsuModHardRock() }, 1.09],
|
||||
[new Mod[] { new OsuModHardRock() }, 1.06],
|
||||
[new Mod[] { new OsuModSuddenDeath() }, 1],
|
||||
[new Mod[] { new OsuModPerfect() }, 1],
|
||||
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } }, 1.000],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.05 } } }, 1.000],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.10 } } }, 1.036],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.15 } } }, 1.036],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.20 } } }, 1.082],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } }, 1.082],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.30 } } }, 1.128],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.35 } } }, 1.128],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.40 } } }, 1.174],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.45 } } }, 1.174],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.50 } } }, 1.230],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.55 } } }, 1.230],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.60 } } }, 1.266],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.65 } } }, 1.266],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.70 } } }, 1.312],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.75 } } }, 1.312],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.80 } } }, 1.358],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.85 } } }, 1.358],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.90 } } }, 1.404],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.95 } } }, 1.404],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 2.00 } } }, 1.450],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } }, 1.00],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.05 } } }, 1.00],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.10 } } }, 1.02],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.15 } } }, 1.02],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.20 } } }, 1.04],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } }, 1.04],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.30 } } }, 1.06],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.35 } } }, 1.06],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.40 } } }, 1.08],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.45 } } }, 1.08],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.50 } } }, 1.10],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.55 } } }, 1.10],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.60 } } }, 1.12],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.65 } } }, 1.12],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.70 } } }, 1.14],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.75 } } }, 1.14],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.80 } } }, 1.16],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.85 } } }, 1.16],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.90 } } }, 1.18],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.95 } } }, 1.18],
|
||||
[new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 2.00 } } }, 1.20],
|
||||
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.01 } } }, 1.000],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.05 } } }, 1.000],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.10 } } }, 1.036],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.15 } } }, 1.036],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.20 } } }, 1.082],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.25 } } }, 1.082],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.30 } } }, 1.128],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.35 } } }, 1.128],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.40 } } }, 1.174],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.45 } } }, 1.174],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.50 } } }, 1.230],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.55 } } }, 1.230],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.60 } } }, 1.266],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.65 } } }, 1.266],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.70 } } }, 1.312],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.75 } } }, 1.312],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.80 } } }, 1.358],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.85 } } }, 1.358],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.90 } } }, 1.404],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.95 } } }, 1.404],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 2.00 } } }, 1.450],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.01 } } }, 1.00],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.05 } } }, 1.00],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.10 } } }, 1.02],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.15 } } }, 1.02],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.20 } } }, 1.04],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.25 } } }, 1.04],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.30 } } }, 1.06],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.35 } } }, 1.06],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.40 } } }, 1.08],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.45 } } }, 1.08],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.50 } } }, 1.10],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.55 } } }, 1.10],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.60 } } }, 1.12],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.65 } } }, 1.12],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.70 } } }, 1.14],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.75 } } }, 1.14],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.80 } } }, 1.16],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.85 } } }, 1.16],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.90 } } }, 1.18],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 1.95 } } }, 1.18],
|
||||
[new Mod[] { new OsuModNightcore { SpeedChange = { Value = 2.00 } } }, 1.20],
|
||||
|
||||
[new Mod[] { new OsuModHidden() }, 1.04],
|
||||
[new Mod[] { new OsuModHidden { OnlyFadeApproachCircles = { Value = true } } }, 1.02],
|
||||
[new Mod[] { new OsuModHidden() }, 1.06],
|
||||
[new Mod[] { new OsuModHidden { OnlyFadeApproachCircles = { Value = true } } }, 1],
|
||||
|
||||
[new Mod[] { new OsuModTraceable() }, 1.02],
|
||||
[new Mod[] { new OsuModTraceable() }, 1],
|
||||
|
||||
[new Mod[] { new OsuModFlashlight() }, 1.2],
|
||||
[new Mod[] { new OsuModFlashlight { SizeMultiplier = { Value = 0.5f } } }, 1.2],
|
||||
[new Mod[] { new OsuModFlashlight { SizeMultiplier = { Value = 0.9f } } }, 1.2],
|
||||
[new Mod[] { new OsuModFlashlight { SizeMultiplier = { Value = 1.1f } } }, 1.18],
|
||||
[new Mod[] { new OsuModFlashlight { SizeMultiplier = { Value = 1.5f } } }, 1.1],
|
||||
[new Mod[] { new OsuModFlashlight { SizeMultiplier = { Value = 1.9f } } }, 1.02],
|
||||
[new Mod[] { new OsuModFlashlight { SizeMultiplier = { Value = 2f } } }, 1.02],
|
||||
[new Mod[] { new OsuModFlashlight { ComboBasedSize = { Value = false } } }, 1.04],
|
||||
[new Mod[] { new OsuModFlashlight { SizeMultiplier = { Value = 1.9f }, ComboBasedSize = { Value = false } } }, 1.004],
|
||||
[new Mod[] { new OsuModFlashlight() }, 1.12],
|
||||
[new Mod[] { new OsuModFlashlight { ComboBasedSize = { Value = false } } }, 1],
|
||||
|
||||
[new Mod[] { new OsuModBlinds() }, 1.24],
|
||||
[new Mod[] { new OsuModBlinds() }, 1.12],
|
||||
[new Mod[] { new OsuModStrictTracking() }, 1],
|
||||
[new Mod[] { new OsuModAccuracyChallenge() }, 1],
|
||||
|
||||
@@ -130,13 +114,10 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
#region Conversion
|
||||
|
||||
[new Mod[] { new OsuModTargetPractice() }, 0.01],
|
||||
[new Mod[] { new OsuModDifficultyAdjust() }, 1],
|
||||
|
||||
[new Mod[] { new OsuModClassic() }, 0.985],
|
||||
[new Mod[] { new OsuModClassic { ClassicNoteLock = { Value = false } } }, 0.96],
|
||||
|
||||
[new Mod[] { new OsuModRandom() }, 0.7],
|
||||
[new Mod[] { new OsuModTargetPractice() }, 0.1],
|
||||
[new Mod[] { new OsuModDifficultyAdjust() }, 0.5],
|
||||
[new Mod[] { new OsuModClassic() }, 0.96],
|
||||
[new Mod[] { new OsuModRandom() }, 1],
|
||||
[new Mod[] { new OsuModMirror() }, 1],
|
||||
[new Mod[] { new OsuModAlternate() }, 1],
|
||||
[new Mod[] { new OsuModSingleTap() }, 1],
|
||||
@@ -149,7 +130,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[new Mod[] { new OsuModCinema() }, 1],
|
||||
[new Mod[] { new OsuModRelax() }, 0.1],
|
||||
[new Mod[] { new OsuModAutopilot() }, 0.1],
|
||||
[new Mod[] { new OsuModSpunOut() }, 0.95],
|
||||
[new Mod[] { new OsuModSpunOut() }, 0.9],
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -159,36 +140,19 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[new Mod[] { new OsuModWiggle() }, 1],
|
||||
[new Mod[] { new OsuModSpinIn() }, 1],
|
||||
[new Mod[] { new OsuModGrow() }, 1],
|
||||
|
||||
[new Mod[] { new OsuModDeflate() }, 1],
|
||||
[new Mod[] { new OsuModDeflate { StartScale = { Value = 5 } } }, 0.94],
|
||||
|
||||
[new Mod[] { new ModWindUp() }, 0.8 * 1 + 0.2 * 1.230],
|
||||
[new Mod[] { new ModWindUp { InitialRate = { Value = 0.7 }, FinalRate = { Value = 1.2 } } }, 0.8 * 0.48 + 0.2 * 1.082],
|
||||
[new Mod[] { new ModWindUp { InitialRate = { Value = 0.7 }, FinalRate = { Value = 0.9 } } }, 0.8 * 0.48 + 0.2 * 0.76],
|
||||
[new Mod[] { new ModWindUp { InitialRate = { Value = 1.1 }, FinalRate = { Value = 1.4 } } }, 0.8 * 1.036 + 0.2 * 1.174],
|
||||
|
||||
[new Mod[] { new ModWindDown() }, 0.8 * 0.55 + 0.2 * 1],
|
||||
[new Mod[] { new ModWindDown { InitialRate = { Value = 1.2 }, FinalRate = { Value = 0.7 } } }, 0.8 * 0.48 + 0.2 * 1.082],
|
||||
[new Mod[] { new ModWindDown { InitialRate = { Value = 0.9 }, FinalRate = { Value = 0.7 } } }, 0.8 * 0.48 + 0.2 * 0.76],
|
||||
[new Mod[] { new ModWindDown { InitialRate = { Value = 1.4 }, FinalRate = { Value = 1.1 } } }, 0.8 * 1.036 + 0.2 * 1.174],
|
||||
|
||||
[new Mod[] { new ModWindUp() }, 0.5],
|
||||
[new Mod[] { new ModWindDown() }, 0.5],
|
||||
[new Mod[] { new OsuModBarrelRoll() }, 1],
|
||||
[new Mod[] { new OsuModApproachDifferent() }, 0.7],
|
||||
[new Mod[] { new OsuModApproachDifferent() }, 1],
|
||||
[new Mod[] { new OsuModMuted() }, 1],
|
||||
[new Mod[] { new OsuModNoScope() }, 1],
|
||||
|
||||
[new Mod[] { new OsuModMagnetised() }, 0.4],
|
||||
[new Mod[] { new OsuModMagnetised { AttractionStrength = { Value = 0.05f } } }, 0.67],
|
||||
[new Mod[] { new OsuModMagnetised { AttractionStrength = { Value = 0.2f } } }, 0.58],
|
||||
[new Mod[] { new OsuModMagnetised { AttractionStrength = { Value = 0.7f } } }, 0.28],
|
||||
[new Mod[] { new OsuModMagnetised { AttractionStrength = { Value = 1 } } }, 0.1],
|
||||
|
||||
[new Mod[] { new OsuModMagnetised() }, 0.5],
|
||||
[new Mod[] { new OsuModRepel() }, 1],
|
||||
[new Mod[] { new ModAdaptiveSpeed() }, 0.1],
|
||||
[new Mod[] { new ModAdaptiveSpeed() }, 0.5],
|
||||
[new Mod[] { new OsuModFreezeFrame() }, 1],
|
||||
[new Mod[] { new OsuModBubbles() }, 1],
|
||||
[new Mod[] { new OsuModSynesthesia() }, 0.99],
|
||||
[new Mod[] { new OsuModSynesthesia() }, 0.8],
|
||||
[new Mod[] { new OsuModDepth() }, 1],
|
||||
[new Mod[] { new OsuModBloom() }, 1],
|
||||
|
||||
@@ -203,24 +167,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
#region Combinations
|
||||
|
||||
[new Mod[] { new OsuModHidden(), new OsuModHardRock() }, 1.04 * 1.09],
|
||||
|
||||
[new Mod[] { new OsuModHidden(), new OsuModWiggle() }, 1.02],
|
||||
[new Mod[] { new OsuModHidden(), new OsuModGrow() }, 1.02],
|
||||
[new Mod[] { new OsuModHidden(), new OsuModDeflate() }, 1.02],
|
||||
[new Mod[] { new OsuModHidden(), new OsuModDeflate { StartScale = { Value = 4 } } }, 1.02 * 0.96],
|
||||
[new Mod[] { new OsuModHidden(), new OsuModRepel() }, 1.02],
|
||||
[new Mod[] { new OsuModHidden { OnlyFadeApproachCircles = { Value = true } }, new OsuModRepel() }, 1],
|
||||
[new Mod[] { new OsuModHidden(), new OsuModDepth() }, 1.02],
|
||||
[new Mod[] { new OsuModHidden(), new OsuModDepth(), new OsuModHardRock() }, 1.02 * 1.09],
|
||||
|
||||
[new Mod[] { new OsuModHidden(), new OsuModBlinds() }, 1.24],
|
||||
[new Mod[] { new OsuModHidden(), new OsuModBlinds(), new OsuModHardRock() }, 1.24 * 1.09],
|
||||
|
||||
[new Mod[] { new OsuModTraceable(), new OsuModBlinds() }, 1.24],
|
||||
[new Mod[] { new OsuModTraceable(), new OsuModBlinds(), new OsuModHardRock() }, 1.24 * 1.09],
|
||||
|
||||
[new Mod[] { new OsuModFlashlight(), new OsuModFreezeFrame() }, 1.1],
|
||||
[new Mod[] { new OsuModHidden(), new OsuModHardRock() }, 1.06 * 1.06],
|
||||
|
||||
#endregion
|
||||
];
|
||||
@@ -228,70 +175,5 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[TestCaseSource(nameof(test_cases))]
|
||||
public void TestMultipliers(Mod[] mods, double expectedMultiplier)
|
||||
=> TestModCombination(mods, expectedMultiplier);
|
||||
|
||||
[TestCase(null, null, null, null, 1)]
|
||||
[TestCase(2.9f, null, null, null, 0.95)]
|
||||
[TestCase(3.1f, null, null, null, 0.95)]
|
||||
[TestCase(null, 3.9f, null, null, 0.95)]
|
||||
[TestCase(null, 4.1f, null, null, 0.95)]
|
||||
[TestCase(null, null, 4.9f, null, 0.95)]
|
||||
[TestCase(null, null, 5.1f, null, 0.95)]
|
||||
[TestCase(null, null, null, 5.9f, 0.95)]
|
||||
[TestCase(null, null, null, 6.1f, 0.95)]
|
||||
[TestCase(2.9f, 3.9f, null, null, 0.95 * 0.95)]
|
||||
[TestCase(2.9f, 3.9f, 4.9f, null, 0.95 * 0.95 * 0.95)]
|
||||
[TestCase(2.9f, 3.9f, 4.9f, 5.9f, 0.95 * 0.95 * 0.95 * 0.95)]
|
||||
[TestCase(0.0f, null, null, null, 0.1)]
|
||||
[TestCase(0.0f, 0.0f, 0.0f, 0.0f, 0.1)]
|
||||
public void TestDifficultyAdjust(float? cs, float? ar, float? od, float? hp, double expectedMultiplier)
|
||||
{
|
||||
var difficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 3,
|
||||
ApproachRate = 4,
|
||||
OverallDifficulty = 5,
|
||||
DrainRate = 6,
|
||||
};
|
||||
var mod = new OsuModDifficultyAdjust
|
||||
{
|
||||
CircleSize = { Value = cs },
|
||||
ApproachRate = { Value = ar },
|
||||
OverallDifficulty = { Value = od },
|
||||
DrainRate = { Value = hp },
|
||||
};
|
||||
|
||||
var calculator = Ruleset.CreateScoreMultiplierCalculator(new ScoreMultiplierContext(difficulty));
|
||||
Assert.That(calculator.CalculateFor([mod]), Is.EqualTo(expectedMultiplier).Within(Precision.FLOAT_EPSILON));
|
||||
}
|
||||
|
||||
[TestCase(30000001, 0.96)]
|
||||
[TestCase(30000009, 0.96)]
|
||||
[TestCase(30000016, 0.96)]
|
||||
[TestCase(30000017, 0.985)]
|
||||
[TestCase(null, 0.985)]
|
||||
public void TestClassicMultiplierVersioning(int? totalScoreVersion, double expectedMultiplier)
|
||||
{
|
||||
var scoreInfo = totalScoreVersion != null ? new ScoreInfo { TotalScoreVersion = totalScoreVersion.Value } : null;
|
||||
var calculator = Ruleset.CreateScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty(), scoreInfo));
|
||||
Assert.That(calculator.CalculateFor([new OsuModClassic()]), Is.EqualTo(expectedMultiplier).Within(Precision.DOUBLE_EPSILON));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerySmallModMultiplier()
|
||||
{
|
||||
var mods = new Mod[]
|
||||
{
|
||||
new OsuModEasy { Retries = { Value = 10 } },
|
||||
new OsuModNoFail(),
|
||||
new OsuModHalfTime { SpeedChange = { Value = 0.5 } },
|
||||
new OsuModTargetPractice(),
|
||||
new OsuModClassic { ClassicNoteLock = { Value = false } },
|
||||
new OsuModDeflate { StartScale = { Value = 25 } },
|
||||
new OsuModMagnetised { AttractionStrength = { Value = 1 } },
|
||||
new OsuModSynesthesia(),
|
||||
};
|
||||
var calculator = Ruleset.CreateScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty()));
|
||||
Assert.That(calculator.CalculateFor(mods), Is.GreaterThan(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,13 +234,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
}
|
||||
}
|
||||
|
||||
public override ScoreMultiplierCalculator CreateScoreMultiplierCalculator(ScoreMultiplierContext context)
|
||||
{
|
||||
if (context.Score != null && context.Score.TotalScoreVersion < 30000017)
|
||||
return new OsuScoreMultiplierCalculatorV1(context);
|
||||
|
||||
return new OsuScoreMultiplierCalculatorV2(context);
|
||||
}
|
||||
public override ScoreMultiplierCalculator CreateScoreMultiplierCalculator(ScoreMultiplierContext context) => new OsuScoreMultiplierCalculator(context);
|
||||
|
||||
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetOsu };
|
||||
|
||||
|
||||
+2
-2
@@ -7,9 +7,9 @@ using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Scoring
|
||||
{
|
||||
public class OsuScoreMultiplierCalculatorV1 : ScoreMultiplierCalculator
|
||||
public class OsuScoreMultiplierCalculator : ScoreMultiplierCalculator
|
||||
{
|
||||
public OsuScoreMultiplierCalculatorV1(ScoreMultiplierContext context)
|
||||
public OsuScoreMultiplierCalculator(ScoreMultiplierContext context)
|
||||
: base(context)
|
||||
{
|
||||
#region Difficulty Reduction
|
||||
@@ -1,200 +0,0 @@
|
||||
// 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;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Scoring
|
||||
{
|
||||
public class OsuScoreMultiplierCalculatorV2 : ScoreMultiplierCalculator
|
||||
{
|
||||
public OsuScoreMultiplierCalculatorV2(ScoreMultiplierContext context)
|
||||
: base(context)
|
||||
{
|
||||
#region Difficulty Reduction
|
||||
|
||||
Single<OsuModEasy>(hasMultiplier: easyMultiplier);
|
||||
Single<OsuModNoFail>(hasMultiplier: 0.5);
|
||||
Single<OsuModHalfTime>(hasMultiplier: halfTime => halfTimeMultiplier(halfTime.SpeedChange.Value));
|
||||
Single<OsuModDaycore>(hasMultiplier: daycore => halfTimeMultiplier(daycore.SpeedChange.Value));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Difficulty Increase
|
||||
|
||||
Single<OsuModHardRock>(hasMultiplier: 1.09);
|
||||
// Sudden Death (1.0x)
|
||||
// Perfect (1.0x)
|
||||
Single<OsuModDoubleTime>(hasMultiplier: doubleTime => doubleTimeMultiplier(doubleTime.SpeedChange.Value));
|
||||
Single<OsuModNightcore>(hasMultiplier: nightcore => doubleTimeMultiplier(nightcore.SpeedChange.Value));
|
||||
|
||||
const double blinds_multiplier = 1.24;
|
||||
|
||||
Combination<OsuModHidden, OsuModBlinds>(hasMultiplier: (_, _) => blinds_multiplier);
|
||||
|
||||
Combination<OsuModHidden, OsuModWiggle>(hasMultiplier: (hidden, _) => hiddenMultiplier(hidden, otherModsProvideTimingInfo: true));
|
||||
Combination<OsuModHidden, OsuModGrow>(hasMultiplier: (hidden, _) => hiddenMultiplier(hidden, otherModsProvideTimingInfo: true));
|
||||
Combination<OsuModHidden, OsuModDeflate>(hasMultiplier: (hidden, deflate) => hiddenMultiplier(hidden, otherModsProvideTimingInfo: true) * deflateMultiplier(deflate));
|
||||
Combination<OsuModHidden, OsuModRepel>(hasMultiplier: (hidden, _) => hiddenMultiplier(hidden, otherModsProvideTimingInfo: true));
|
||||
Combination<OsuModHidden, OsuModDepth>(hasMultiplier: (hidden, _) => hiddenMultiplier(hidden, otherModsProvideTimingInfo: true));
|
||||
|
||||
Single<OsuModHidden>(hasMultiplier: hidden => hiddenMultiplier(hidden, otherModsProvideTimingInfo: false));
|
||||
|
||||
Combination<OsuModTraceable, OsuModBlinds>(hasMultiplier: (_, _) => blinds_multiplier);
|
||||
Single<OsuModTraceable>(hasMultiplier: 1.02);
|
||||
|
||||
Combination<OsuModFlashlight, OsuModFreezeFrame>(hasMultiplier: (flashlight, _) => 1 + (flashlightMultiplier(flashlight) - 1) / 2);
|
||||
Single<OsuModFlashlight>(hasMultiplier: flashlightMultiplier);
|
||||
|
||||
Single<OsuModBlinds>(hasMultiplier: blinds_multiplier);
|
||||
// Strict Tracking (1.0x)
|
||||
// Accuracy Challenge (1.0x)
|
||||
|
||||
#endregion
|
||||
|
||||
#region Conversion
|
||||
|
||||
Single<OsuModTargetPractice>(hasMultiplier: 0.01);
|
||||
Single<OsuModDifficultyAdjust>(hasMultiplier: difficultyAdjust => difficultyAdjustMultiplier(difficultyAdjust, Context.BeatmapDifficultyWithoutMods));
|
||||
Single<OsuModClassic>(hasMultiplier: classic => classic.ClassicNoteLock.Value ? 0.985 : 0.96);
|
||||
Single<OsuModRandom>(hasMultiplier: 0.7);
|
||||
// Mirror (1.0x)
|
||||
// Alternate (1.0x)
|
||||
// Single Tap (1.0x)
|
||||
|
||||
#endregion
|
||||
|
||||
#region Automation
|
||||
|
||||
// Autoplay (1.0x)
|
||||
// Cinema (1.0x)
|
||||
Single<OsuModRelax>(hasMultiplier: 0.1);
|
||||
Single<OsuModAutopilot>(hasMultiplier: 0.1);
|
||||
Single<OsuModSpunOut>(hasMultiplier: 0.95);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fun
|
||||
|
||||
// Transform (1.0x)
|
||||
// Wiggle (1.0x)
|
||||
// Spin In (1.0x)
|
||||
// Grow (1.0x)
|
||||
Single<OsuModDeflate>(hasMultiplier: deflateMultiplier);
|
||||
Single<ModWindUp>(hasMultiplier: timeRampMultiplier);
|
||||
Single<ModWindDown>(hasMultiplier: timeRampMultiplier);
|
||||
// Barrel Roll (1.0x)
|
||||
Single<OsuModApproachDifferent>(hasMultiplier: 0.7);
|
||||
// Muted (1.0x)
|
||||
// No Scope (1.0x)
|
||||
Single<OsuModMagnetised>(hasMultiplier: magnetised => 0.7 - magnetised.AttractionStrength.Value * 0.6);
|
||||
// Repel (1.0x)
|
||||
Single<ModAdaptiveSpeed>(hasMultiplier: 0.1);
|
||||
// Freeze Frame (1.0x)
|
||||
// Bubbles (1.0x)
|
||||
Single<OsuModSynesthesia>(hasMultiplier: 0.99);
|
||||
// Depth (1.0x)
|
||||
// Bloom (1.0x)
|
||||
|
||||
#endregion
|
||||
|
||||
#region System
|
||||
|
||||
// Touch Device (1.0x)
|
||||
// Score V2 (1.0x)
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
private static double easyMultiplier(OsuModEasy easy)
|
||||
{
|
||||
// 0.8x base multiplier
|
||||
// Reduce by 0.1x per extra life
|
||||
double value = 0.8 - Math.Max(0, 0.1 * (easy.Retries.Value - easy.Retries.Default));
|
||||
|
||||
return Math.Max(0.4, value);
|
||||
}
|
||||
|
||||
private static double halfTimeMultiplier(double speedChange)
|
||||
{
|
||||
// 0.2x at 0.5x speed, +0.07x per 0.05x speed increment.
|
||||
// Default HT (0.75x) = 0.55
|
||||
return (int)(speedChange * 20) / 20.0 * 1.4 - 0.5;
|
||||
}
|
||||
|
||||
private static double doubleTimeMultiplier(double speedChange)
|
||||
{
|
||||
// Floor to the nearest multiple of 0.1.
|
||||
double value = (int)(speedChange * 10) / 10.0;
|
||||
|
||||
// 0.01 penalty for non-default rates.
|
||||
double penalty = value != 1.5 && value != 1.0 ? 0.01 : 0.0;
|
||||
|
||||
// Linear from 1.0 to 1.46, minus the penalty.
|
||||
// Default DT (1.5x) = 1.23
|
||||
return (value - 1) * 0.46 + 1 - penalty;
|
||||
}
|
||||
|
||||
private static double hiddenMultiplier(OsuModHidden hidden, bool otherModsProvideTimingInfo)
|
||||
{
|
||||
double value = 1.04;
|
||||
|
||||
if (hidden.OnlyFadeApproachCircles.Value)
|
||||
value -= 0.02;
|
||||
|
||||
if (otherModsProvideTimingInfo)
|
||||
value -= 0.02;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static double flashlightMultiplier(OsuModFlashlight flashlight)
|
||||
{
|
||||
// Multiplier of 1.2x, reduced by 0.02 per 0.1 increase in flashlight size.
|
||||
double value = Math.Max(1.02, Math.Min(1.2, 1.2 - 0.2 * (flashlight.SizeMultiplier.Value - 1)));
|
||||
|
||||
if (!flashlight.ComboBasedSize.Value)
|
||||
value = 1 + (value - 1) / 5;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static double difficultyAdjustMultiplier(OsuModDifficultyAdjust difficultyAdjust, IBeatmapDifficultyInfo beatmapDifficulty)
|
||||
{
|
||||
double selectedCircleSize = difficultyAdjust.CircleSize.Value ?? beatmapDifficulty.CircleSize;
|
||||
double selectedDrainRate = difficultyAdjust.DrainRate.Value ?? beatmapDifficulty.DrainRate;
|
||||
double selectedOverallDifficulty = difficultyAdjust.OverallDifficulty.Value ?? beatmapDifficulty.OverallDifficulty;
|
||||
double selectedApproachRate = difficultyAdjust.ApproachRate.Value ?? beatmapDifficulty.ApproachRate;
|
||||
|
||||
double csDifference = Math.Abs(selectedCircleSize - beatmapDifficulty.CircleSize);
|
||||
double hpDifference = Math.Abs(selectedDrainRate - beatmapDifficulty.DrainRate);
|
||||
double odDifference = Math.Abs(selectedOverallDifficulty - beatmapDifficulty.OverallDifficulty);
|
||||
double arDifference = Math.Abs(selectedApproachRate - beatmapDifficulty.ApproachRate);
|
||||
|
||||
// Per parameter, reduce multiplier by 0.05x per 0.1 change.
|
||||
double csMultiplier = Math.Max(0.1, 1.0 - csDifference * 0.5);
|
||||
double hpMultiplier = Math.Max(0.1, 1.0 - hpDifference * 0.5);
|
||||
double odMultiplier = Math.Max(0.1, 1.0 - odDifference * 0.5);
|
||||
double arMultiplier = Math.Max(0.1, 1.0 - arDifference * 0.5);
|
||||
|
||||
return Math.Max(0.1, csMultiplier * hpMultiplier * odMultiplier * arMultiplier);
|
||||
}
|
||||
|
||||
private static double timeRampMultiplier(ModTimeRamp timeRamp)
|
||||
{
|
||||
double minSpeed = Math.Min(timeRamp.InitialRate.Value, timeRamp.FinalRate.Value);
|
||||
double maxSpeed = Math.Max(timeRamp.InitialRate.Value, timeRamp.FinalRate.Value);
|
||||
|
||||
double minMultiplier = minSpeed < 1 ? halfTimeMultiplier(minSpeed) : doubleTimeMultiplier(minSpeed);
|
||||
double maxMultiplier = maxSpeed < 1 ? halfTimeMultiplier(maxSpeed) : doubleTimeMultiplier(maxSpeed);
|
||||
|
||||
return 0.8 * minMultiplier + 0.2 * maxMultiplier;
|
||||
}
|
||||
|
||||
private static double deflateMultiplier(OsuModDeflate deflate)
|
||||
=> 1.0 - Math.Max(0, 0.02 * (deflate.StartScale.Value - deflate.StartScale.Default));
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,8 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Mods;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Tests.Rulesets;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Tests
|
||||
@@ -117,7 +113,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
|
||||
[new Mod[] { new TaikoModRandom() }, 1],
|
||||
[new Mod[] { new TaikoModDifficultyAdjust() }, 0.5],
|
||||
[new Mod[] { new TaikoModClassic() }, 1],
|
||||
[new Mod[] { new TaikoModClassic() }, 0.96],
|
||||
[new Mod[] { new TaikoModSwap() }, 1],
|
||||
[new Mod[] { new TaikoModSingleTap() }, 1],
|
||||
[new Mod[] { new TaikoModConstantSpeed() }, 0.9],
|
||||
@@ -157,17 +153,5 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
[TestCaseSource(nameof(test_cases))]
|
||||
public void TestMultipliers(Mod[] mods, double expectedMultiplier)
|
||||
=> TestModCombination(mods, expectedMultiplier);
|
||||
|
||||
[TestCase(30000001, 0.96)]
|
||||
[TestCase(30000009, 0.96)]
|
||||
[TestCase(30000016, 0.96)]
|
||||
[TestCase(30000017, 1)]
|
||||
[TestCase(null, 1)]
|
||||
public void TestClassicMultiplierVersioning(int? totalScoreVersion, double expectedMultiplier)
|
||||
{
|
||||
var scoreInfo = totalScoreVersion != null ? new ScoreInfo { TotalScoreVersion = totalScoreVersion.Value } : null;
|
||||
var calculator = Ruleset.CreateScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty(), scoreInfo));
|
||||
Assert.That(calculator.CalculateFor([new TaikoModClassic()]), Is.EqualTo(expectedMultiplier).Within(Precision.DOUBLE_EPSILON));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Mods;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Scoring
|
||||
{
|
||||
@@ -40,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
|
||||
|
||||
// Random
|
||||
Single<TaikoModDifficultyAdjust>(hasMultiplier: 0.5);
|
||||
Single<TaikoModClassic>(hasMultiplier: _ => classicMultiplier(Context.Score));
|
||||
Single<TaikoModClassic>(hasMultiplier: 0.96);
|
||||
// Swap
|
||||
// Single Tap
|
||||
Single<TaikoModConstantSpeed>(hasMultiplier: 0.9);
|
||||
@@ -84,13 +83,5 @@ namespace osu.Game.Rulesets.Taiko.Scoring
|
||||
else
|
||||
return 0.6 + value;
|
||||
}
|
||||
|
||||
private static double classicMultiplier(ScoreInfo? score)
|
||||
{
|
||||
if (score != null && score.TotalScoreVersion < 30000017)
|
||||
return 0.96;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,7 +552,6 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore("Temporarily ignored pending correct addressing of treatment of `TotalScoreVersion` in replays")]
|
||||
public void TestTotalScoreWithoutModsBackwardsPopulatedIfMissing()
|
||||
{
|
||||
var ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
@@ -228,29 +228,21 @@ namespace osu.Game.Tests.Mods
|
||||
[Test]
|
||||
public void TestFormatScoreMultiplier()
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(ModUtils.FormatScoreMultiplier(0.9999).ToString(), Is.EqualTo("0.99x"));
|
||||
Assert.That(ModUtils.FormatScoreMultiplier(1.0).ToString(), Is.EqualTo("1.00x"));
|
||||
Assert.That(ModUtils.FormatScoreMultiplier(1.0001).ToString(), Is.EqualTo("1.01x"));
|
||||
ClassicAssert.AreEqual(ModUtils.FormatScoreMultiplier(0.9999).ToString(), "0.99x");
|
||||
ClassicAssert.AreEqual(ModUtils.FormatScoreMultiplier(1.0).ToString(), "1.00x");
|
||||
ClassicAssert.AreEqual(ModUtils.FormatScoreMultiplier(1.0001).ToString(), "1.01x");
|
||||
|
||||
Assert.That(ModUtils.FormatScoreMultiplier(0.899999999999999).ToString(), Is.EqualTo("0.90x"));
|
||||
Assert.That(ModUtils.FormatScoreMultiplier(0.9).ToString(), Is.EqualTo("0.90x"));
|
||||
Assert.That(ModUtils.FormatScoreMultiplier(0.900000000000001).ToString(), Is.EqualTo("0.90x"));
|
||||
ClassicAssert.AreEqual(ModUtils.FormatScoreMultiplier(0.899999999999999).ToString(), "0.90x");
|
||||
ClassicAssert.AreEqual(ModUtils.FormatScoreMultiplier(0.9).ToString(), "0.90x");
|
||||
ClassicAssert.AreEqual(ModUtils.FormatScoreMultiplier(0.900000000000001).ToString(), "0.90x");
|
||||
|
||||
Assert.That(ModUtils.FormatScoreMultiplier(1.099999999999999).ToString(), Is.EqualTo("1.10x"));
|
||||
Assert.That(ModUtils.FormatScoreMultiplier(1.1).ToString(), Is.EqualTo("1.10x"));
|
||||
Assert.That(ModUtils.FormatScoreMultiplier(1.100000000000001).ToString(), Is.EqualTo("1.10x"));
|
||||
ClassicAssert.AreEqual(ModUtils.FormatScoreMultiplier(1.099999999999999).ToString(), "1.10x");
|
||||
ClassicAssert.AreEqual(ModUtils.FormatScoreMultiplier(1.1).ToString(), "1.10x");
|
||||
ClassicAssert.AreEqual(ModUtils.FormatScoreMultiplier(1.100000000000001).ToString(), "1.10x");
|
||||
|
||||
Assert.That(ModUtils.FormatScoreMultiplier(1.045).ToString(), Is.EqualTo("1.05x"));
|
||||
Assert.That(ModUtils.FormatScoreMultiplier(1.05).ToString(), Is.EqualTo("1.05x"));
|
||||
Assert.That(ModUtils.FormatScoreMultiplier(1.055).ToString(), Is.EqualTo("1.06x"));
|
||||
|
||||
Assert.That(ModUtils.FormatScoreMultiplier(1.1799999952316285).ToString(), Is.EqualTo("1.18x"));
|
||||
Assert.That(ModUtils.FormatScoreMultiplier(1.1599999904632567).ToString(), Is.EqualTo("1.16x"));
|
||||
Assert.That(ModUtils.FormatScoreMultiplier(1.1400000095367431).ToString(), Is.EqualTo("1.14x"));
|
||||
Assert.That(ModUtils.FormatScoreMultiplier(1.1200000047683716).ToString(), Is.EqualTo("1.12x"));
|
||||
});
|
||||
ClassicAssert.AreEqual(ModUtils.FormatScoreMultiplier(1.045).ToString(), "1.05x");
|
||||
ClassicAssert.AreEqual(ModUtils.FormatScoreMultiplier(1.05).ToString(), "1.05x");
|
||||
ClassicAssert.AreEqual(ModUtils.FormatScoreMultiplier(1.055).ToString(), "1.06x");
|
||||
}
|
||||
|
||||
private static readonly object[] multiplayer_mod_test_scenarios =
|
||||
|
||||
@@ -321,13 +321,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
ClickButtonWhenEnabled<UserModSelectButton>();
|
||||
AddUntilStep("mod select shows unranked", () => this.ChildrenOfType<RankingInformationDisplay>().Single().Ranked.Value == false);
|
||||
AddAssert("score multiplier = 1.45", () => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(1.45).Within(0.01));
|
||||
AddAssert("score multiplier = 1.20", () => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(1.2).Within(0.01));
|
||||
|
||||
AddStep("select flashlight", () => this.ChildrenOfType<MultiplayerUserModSelectOverlay>().Single().ChildrenOfType<ModPanel>().Single(m => m.Mod is ModFlashlight).TriggerClick());
|
||||
AddAssert("score multiplier = 1.74", () => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(1.74).Within(0.01));
|
||||
AddAssert("score multiplier = 1.35", () => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(1.35).Within(0.01));
|
||||
|
||||
AddStep("change flashlight setting", () => ((OsuModFlashlight)this.ChildrenOfType<MultiplayerUserModSelectOverlay>().Single().SelectedMods.Value.Single()).ComboBasedSize.Value = false);
|
||||
AddAssert("score multiplier = 1.51", () => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(1.51).Within(0.01));
|
||||
AddStep("change flashlight setting", () => ((OsuModFlashlight)this.ChildrenOfType<MultiplayerUserModSelectOverlay>().Single().SelectedMods.Value.Single()).FollowDelay.Value = 1200);
|
||||
AddAssert("score multiplier = 1.20", () => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(1.2).Within(0.01));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -4,17 +4,14 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.AccountCreation;
|
||||
@@ -97,66 +94,8 @@ namespace osu.Game.Tests.Visual.Online
|
||||
.ChildrenOfType<SettingsButton>().Single().TriggerClick());
|
||||
AddUntilStep("verification screen is present", () => accountCreation.ChildrenOfType<ScreenEmailVerification>().SingleOrDefault()?.IsPresent == true);
|
||||
|
||||
AddStep("verify", () =>
|
||||
accountCreation.ChildrenOfType<ScreenEmailVerification>().Single().ChildrenOfType<TextBox>().Single().Current.Value = "abcdefgh");
|
||||
AddStep("verify", () => ((DummyAPIAccess)API).AuthenticateSecondFactor("abcdefgh"));
|
||||
AddUntilStep("overlay is hidden", () => accountCreation.State.Value == Visibility.Hidden);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFailedVerification()
|
||||
{
|
||||
AddStep("set up API", () => ((DummyAPIAccess)API).HandleRequest = req =>
|
||||
{
|
||||
switch (req)
|
||||
{
|
||||
case VerifySessionRequest verifySessionRequest:
|
||||
if (verifySessionRequest.VerificationKey == "abcdefgh")
|
||||
verifySessionRequest.TriggerSuccess();
|
||||
else
|
||||
{
|
||||
Scheduler.AddDelayed(
|
||||
() => verifySessionRequest.TriggerFailure(new APIException("Incorrect verification code.", null, HttpStatusCode.UnprocessableEntity)),
|
||||
1000);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
AddStep("log out", () => API.Logout());
|
||||
|
||||
AddStep("show manually", () => accountCreation.Show());
|
||||
AddUntilStep("overlay is visible", () => accountCreation.State.Value, () => Is.EqualTo(Visibility.Visible));
|
||||
|
||||
AddStep("click button", () => accountCreation.ChildrenOfType<SettingsButton>().Single().TriggerClick());
|
||||
AddUntilStep("warning screen is present", () => accountCreation.ChildrenOfType<ScreenWarning>().SingleOrDefault()?.IsPresent, () => Is.True);
|
||||
|
||||
AddStep("proceed", () => accountCreation.ChildrenOfType<DangerousSettingsButton>().Single().TriggerClick());
|
||||
AddUntilStep("entry screen is present", () => accountCreation.ChildrenOfType<ScreenEntry>().SingleOrDefault()?.IsPresent, () => Is.True);
|
||||
|
||||
AddStep("input details", () =>
|
||||
{
|
||||
var entryScreen = accountCreation.ChildrenOfType<ScreenEntry>().Single();
|
||||
entryScreen.ChildrenOfType<OsuTextBox>().ElementAt(0).Text = "new_user";
|
||||
entryScreen.ChildrenOfType<OsuTextBox>().ElementAt(1).Text = "new.user@fake.mail";
|
||||
entryScreen.ChildrenOfType<OsuTextBox>().ElementAt(2).Text = "password";
|
||||
});
|
||||
AddStep("click button", () => accountCreation.ChildrenOfType<ScreenEntry>().Single()
|
||||
.ChildrenOfType<SettingsButton>().Single().TriggerClick());
|
||||
AddUntilStep("verification screen is present", () => accountCreation.ChildrenOfType<ScreenEmailVerification>().SingleOrDefault()?.IsPresent, () => Is.True);
|
||||
|
||||
AddStep("fail to verify", () =>
|
||||
accountCreation.ChildrenOfType<ScreenEmailVerification>().Single().ChildrenOfType<TextBox>().Single().Current.Value = "abcdefff");
|
||||
AddUntilStep("overlay is still visible", () => accountCreation.State.Value, () => Is.EqualTo(Visibility.Visible));
|
||||
AddUntilStep("text box back to enabled",
|
||||
() => accountCreation.ChildrenOfType<ScreenEmailVerification>().Single().ChildrenOfType<TextBox>().Single().Current.Disabled, () => Is.False);
|
||||
|
||||
AddStep("verify", () =>
|
||||
accountCreation.ChildrenOfType<ScreenEmailVerification>().Single().ChildrenOfType<TextBox>().Single().Current.Value = "abcdefgh");
|
||||
AddUntilStep("overlay is hidden", () => accountCreation.State.Value, () => Is.EqualTo(Visibility.Hidden));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,11 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Utils;
|
||||
|
||||
@@ -71,19 +73,19 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddStep("Set ruleset", () => footerButtonMods.Ruleset.Value = ruleset.RulesetInfo);
|
||||
|
||||
AddStep(@"Add Hidden", () => changeMods(hiddenMod));
|
||||
assertModsMultiplier(1.04);
|
||||
assertModsMultiplier(ruleset, hiddenMod);
|
||||
|
||||
var hardRockMod = new Mod[] { new OsuModHardRock() };
|
||||
AddStep(@"Add HardRock", () => changeMods(hardRockMod));
|
||||
assertModsMultiplier(1.09);
|
||||
assertModsMultiplier(ruleset, hardRockMod);
|
||||
|
||||
var doubleTimeMod = new Mod[] { new OsuModDoubleTime() };
|
||||
AddStep(@"Add DoubleTime", () => changeMods(doubleTimeMod));
|
||||
assertModsMultiplier(1.23);
|
||||
assertModsMultiplier(ruleset, doubleTimeMod);
|
||||
|
||||
var multipleIncrementMods = new Mod[] { new OsuModDoubleTime(), new OsuModHidden(), new OsuModHardRock() };
|
||||
AddStep(@"Add multiple Mods", () => changeMods(multipleIncrementMods));
|
||||
assertModsMultiplier(1.23 * 1.04 * 1.09);
|
||||
assertModsMultiplier(ruleset, multipleIncrementMods);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -95,60 +97,15 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddStep("Set ruleset", () => footerButtonMods.Ruleset.Value = ruleset.RulesetInfo);
|
||||
|
||||
AddStep(@"Add Easy", () => changeMods(easyMod));
|
||||
assertModsMultiplier(0.8);
|
||||
assertModsMultiplier(ruleset, easyMod);
|
||||
|
||||
var noFailMod = new Mod[] { new OsuModNoFail() };
|
||||
AddStep(@"Add NoFail", () => changeMods(noFailMod));
|
||||
assertModsMultiplier(0.5);
|
||||
assertModsMultiplier(ruleset, noFailMod);
|
||||
|
||||
var multipleDecrementMods = new Mod[] { new OsuModEasy(), new OsuModNoFail() };
|
||||
AddStep(@"Add Multiple Mods", () => changeMods(multipleDecrementMods));
|
||||
assertModsMultiplier(0.8 * 0.5);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDifficultyAdjustMultiplier()
|
||||
{
|
||||
var ruleset = new OsuRuleset();
|
||||
var difficultyAdjustMod = new OsuModDifficultyAdjust();
|
||||
|
||||
AddStep("Set ruleset", () => footerButtonMods.Ruleset.Value = ruleset.RulesetInfo);
|
||||
|
||||
AddStep("Set beatmap", () =>
|
||||
{
|
||||
var beatmap = CreateWorkingBeatmap(ruleset.RulesetInfo);
|
||||
beatmap.BeatmapInfo.Difficulty = new BeatmapDifficulty
|
||||
{
|
||||
ApproachRate = 3,
|
||||
OverallDifficulty = 5,
|
||||
CircleSize = 5,
|
||||
DrainRate = 6,
|
||||
};
|
||||
Beatmap.Value = beatmap;
|
||||
});
|
||||
|
||||
AddStep(@"Set Difficulty Adjust", () => changeMods([difficultyAdjustMod]));
|
||||
assertModsMultiplier(1);
|
||||
|
||||
AddStep("Adjust AR", () => difficultyAdjustMod.ApproachRate.Value = 3.3f);
|
||||
assertModsMultiplier(0.85);
|
||||
|
||||
AddStep("Adjust HP", () => difficultyAdjustMod.DrainRate.Value = 6.5f);
|
||||
assertModsMultiplier(0.6375);
|
||||
|
||||
AddStep("Change beatmap", () =>
|
||||
{
|
||||
var beatmap = CreateWorkingBeatmap(ruleset.RulesetInfo);
|
||||
beatmap.BeatmapInfo.Difficulty = new BeatmapDifficulty
|
||||
{
|
||||
ApproachRate = 3.3f,
|
||||
OverallDifficulty = 8,
|
||||
CircleSize = 8,
|
||||
DrainRate = 6,
|
||||
};
|
||||
Beatmap.Value = beatmap;
|
||||
});
|
||||
assertModsMultiplier(0.75);
|
||||
assertModsMultiplier(ruleset, multipleDecrementMods);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -162,9 +119,11 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
private void changeMods(IReadOnlyList<Mod> mods) => footerButtonMods.Mods.Value = mods;
|
||||
|
||||
private void assertModsMultiplier(double expectedMultiplier)
|
||||
private void assertModsMultiplier(Ruleset ruleset, IEnumerable<Mod> mods)
|
||||
{
|
||||
string expectedValue = ModUtils.FormatScoreMultiplier(expectedMultiplier).ToString();
|
||||
var scoreMultiplierCalculator = ruleset.CreateScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty()));
|
||||
double multiplier = scoreMultiplierCalculator.CalculateFor(mods);
|
||||
string expectedValue = ModUtils.FormatScoreMultiplier(multiplier).ToString();
|
||||
|
||||
AddAssert($"Displayed multiplier is {expectedValue}", () => footerButtonMods.ChildrenOfType<OsuSpriteText>().First(t => t.Text.ToString().Contains('x')).Text.ToString(), () => Is.EqualTo(expectedValue));
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
AddUntilStep("two panels active", () => modSelectOverlay.ChildrenOfType<ModPanel>().Count(panel => panel.Active.Value) == 2);
|
||||
AddAssert("mod multiplier correct", () =>
|
||||
{
|
||||
double multiplier = new OsuScoreMultiplierCalculatorV2(new ScoreMultiplierContext(new BeatmapDifficulty())).CalculateFor(SelectedMods.Value);
|
||||
double multiplier = new OsuScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty())).CalculateFor(SelectedMods.Value);
|
||||
return Precision.AlmostEquals(multiplier, this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value);
|
||||
});
|
||||
assertCustomisationToggleState(disabled: false, active: false);
|
||||
@@ -140,7 +140,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
AddUntilStep("two panels active", () => modSelectOverlay.ChildrenOfType<ModPanel>().Count(panel => panel.Active.Value) == 2);
|
||||
AddAssert("mod multiplier correct", () =>
|
||||
{
|
||||
double multiplier = new OsuScoreMultiplierCalculatorV2(new ScoreMultiplierContext(new BeatmapDifficulty())).CalculateFor(SelectedMods.Value);
|
||||
double multiplier = new OsuScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty())).CalculateFor(SelectedMods.Value);
|
||||
return Precision.AlmostEquals(multiplier, this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value);
|
||||
});
|
||||
assertCustomisationToggleState(disabled: false, active: false);
|
||||
@@ -899,7 +899,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddAssert("difficulty multiplier display shows correct value",
|
||||
() => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(0.2).Within(Precision.DOUBLE_EPSILON));
|
||||
() => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(0.1).Within(Precision.DOUBLE_EPSILON));
|
||||
|
||||
// this is highly unorthodox in a test, but because the `ModSettingChangeTracker` machinery heavily leans on events and object disposal and re-creation,
|
||||
// it is instrumental in the reproduction of the failure scenario that this test is supposed to cover.
|
||||
@@ -909,7 +909,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
AddStep("reset half time speed to default", () => modSelectOverlay.ChildrenOfType<ModCustomisationPanel>().Single()
|
||||
.ChildrenOfType<RevertToDefaultButton<double>>().Single().TriggerClick());
|
||||
AddUntilStep("difficulty multiplier display shows correct value",
|
||||
() => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(0.55).Within(Precision.DOUBLE_EPSILON));
|
||||
() => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(0.3).Within(Precision.DOUBLE_EPSILON));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -1032,51 +1032,6 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
AddAssert("OsuModPerfect panel active", () => getPanelForMod(typeof(OsuModPerfect)).Active.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDifficultyAdjustModMultiplierIsCalculatedCorrectly()
|
||||
{
|
||||
createScreen();
|
||||
AddStep("set mods", () => SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() });
|
||||
AddUntilStep("one panel active", () => modSelectOverlay.ChildrenOfType<ModPanel>().Count(panel => panel.Active.Value), () => Is.EqualTo(1));
|
||||
AddAssert("mod multiplier is correct", () => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value,
|
||||
() => Is.EqualTo(1).Within(Precision.FLOAT_EPSILON));
|
||||
assertCustomisationToggleState(disabled: false, active: false);
|
||||
AddAssert("setting items created", () => modSelectOverlay.ChildrenOfType<ISettingsItem>().Any());
|
||||
|
||||
AddStep("modify DA", () => SelectedMods.Value.OfType<OsuModDifficultyAdjust>().Single().CircleSize.Value = 3.9f);
|
||||
AddAssert("mod multiplier is correct", () => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value,
|
||||
() => Is.EqualTo(0.95).Within(Precision.FLOAT_EPSILON));
|
||||
|
||||
AddStep("replace DA completely", () =>
|
||||
{
|
||||
SelectedMods.Value = new Mod[]
|
||||
{
|
||||
new OsuModDifficultyAdjust
|
||||
{
|
||||
ApproachRate = { Value = 6.8f },
|
||||
OverallDifficulty = { Value = 6.2f }
|
||||
}
|
||||
};
|
||||
});
|
||||
AddAssert("mod multiplier is correct", () => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value,
|
||||
() => Is.EqualTo(0.81).Within(Precision.FLOAT_EPSILON));
|
||||
|
||||
AddStep("change beatmap", () =>
|
||||
{
|
||||
var beatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||
beatmap.BeatmapInfo.Difficulty = new BeatmapDifficulty
|
||||
{
|
||||
ApproachRate = 6,
|
||||
OverallDifficulty = 5,
|
||||
CircleSize = 5,
|
||||
DrainRate = 5,
|
||||
};
|
||||
modSelectOverlay.Beatmap.Value = beatmap;
|
||||
});
|
||||
AddAssert("mod multiplier is correct", () => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value,
|
||||
() => Is.EqualTo(0.24).Within(Precision.FLOAT_EPSILON));
|
||||
}
|
||||
|
||||
private void waitForColumnLoad() => AddUntilStep("all column content loaded", () =>
|
||||
modSelectOverlay.ChildrenOfType<ModColumn>().Any()
|
||||
&& modSelectOverlay.ChildrenOfType<ModColumn>().All(column => column.IsLoaded && column.ItemsLoaded)
|
||||
|
||||
@@ -158,7 +158,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
if (Enabled.Value)
|
||||
{
|
||||
background.FlashOnCommit();
|
||||
background.Flash();
|
||||
button.TriggerClick();
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
current.BindValueChanged(_ =>
|
||||
{
|
||||
updateState();
|
||||
background.FlashOnCommit();
|
||||
background.Flash();
|
||||
|
||||
ValueChanged?.Invoke();
|
||||
});
|
||||
|
||||
@@ -35,7 +35,6 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
|
||||
private readonly Box box;
|
||||
private readonly Box flashLayer;
|
||||
|
||||
private readonly HoverSounds sounds;
|
||||
|
||||
@@ -45,7 +44,6 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
|
||||
Masking = true;
|
||||
CornerRadius = 5;
|
||||
CornerExponent = 2.5f;
|
||||
|
||||
CornerExponent = CORNER_EXPONENT;
|
||||
BorderThickness = BORDER_THICKNESS;
|
||||
@@ -57,11 +55,6 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
Colour = Color4.White,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
flashLayer = new Box
|
||||
{
|
||||
Colour = Colour4.Transparent,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
sounds = new HoverSounds(),
|
||||
};
|
||||
}
|
||||
@@ -74,22 +67,11 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
private void flash(Colour4 flashColour, double duration)
|
||||
public void Flash()
|
||||
{
|
||||
flashLayer.Colour = ColourInfo.GradientVertical(flashColour.Opacity(0), flashColour);
|
||||
flashLayer.FadeOutFromOne(duration, Easing.OutQuint);
|
||||
box.FlashColour(ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark2), 800, Easing.OutQuint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use when indicating that a change in value or a definitive action has occurred.
|
||||
/// </summary>
|
||||
public void FlashOnCommit() => flash(colourProvider.Dark2, 800);
|
||||
|
||||
/// <summary>
|
||||
/// Use when rejecting the user's input as incorrect.
|
||||
/// </summary>
|
||||
public void FlashOnInputError() => flash(Colour4.Red, 200);
|
||||
|
||||
private void updateStyle()
|
||||
{
|
||||
sounds.Enabled.Value = visualStyle != VisualStyle.Disabled;
|
||||
|
||||
@@ -151,6 +151,9 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Masking = true;
|
||||
CornerRadius = 5;
|
||||
|
||||
// We use our own background for more control.
|
||||
Background.Alpha = 0;
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
initialChooserPath = Current.Value?.DirectoryName;
|
||||
placeholderText.Alpha = Current.Value == null ? 1 : 0;
|
||||
filenameText.Text = Current.Value?.Name ?? string.Empty;
|
||||
background.FlashOnCommit();
|
||||
background.Flash();
|
||||
}
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
|
||||
@@ -10,6 +10,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
@@ -122,6 +123,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
public Func<T, LocalisableString> TooltipFormat { get; init; }
|
||||
|
||||
private FormControlBackground background = null!;
|
||||
private Box flashLayer = null!;
|
||||
private FormTextBox.InnerTextBox textBox = null!;
|
||||
private OsuSpriteText valueLabel = null!;
|
||||
private FormFieldCaption captionText = null!;
|
||||
@@ -196,9 +198,18 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Masking = true;
|
||||
CornerRadius = 5;
|
||||
CornerExponent = 2.5f;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
background = new FormControlBackground(),
|
||||
flashLayer = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Colour4.Transparent,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
@@ -244,7 +255,11 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
AlwaysPresent = true,
|
||||
CommitOnFocusLost = true,
|
||||
SelectAllOnFocus = true,
|
||||
OnInputError = background.FlashOnInputError,
|
||||
OnInputError = () =>
|
||||
{
|
||||
flashLayer.Colour = ColourInfo.GradientVertical(colours.Red3.Opacity(0), colours.Red3);
|
||||
flashLayer.FadeOutFromOne(200, Easing.OutQuint);
|
||||
},
|
||||
TabbableContentContainer = tabbableContentContainer,
|
||||
},
|
||||
valueLabel = new TruncatingSpriteText
|
||||
@@ -312,7 +327,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
currentNumberInstantaneous.TriggerChange();
|
||||
current.Value = currentNumberInstantaneous.Value;
|
||||
|
||||
background.FlashOnCommit();
|
||||
background.Flash();
|
||||
}
|
||||
|
||||
private void tryUpdateSliderFromTextBox()
|
||||
|
||||
@@ -5,10 +5,13 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input;
|
||||
@@ -80,6 +83,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
public int? LengthLimit { get; init; }
|
||||
|
||||
private FormControlBackground background = null!;
|
||||
private Box flashLayer = null!;
|
||||
private InnerTextBox textBox = null!;
|
||||
private FormFieldCaption caption = null!;
|
||||
private IFocusManager focusManager = null!;
|
||||
@@ -96,6 +100,11 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
background = new FormControlBackground(),
|
||||
flashLayer = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Colour4.Transparent,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
@@ -124,9 +133,16 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
OnCommit?.Invoke(textBox, newText);
|
||||
|
||||
if (!current.Disabled && !ReadOnly)
|
||||
background.FlashOnCommit();
|
||||
{
|
||||
flashLayer.Colour = ColourInfo.GradientVertical(colourProvider.Dark2.Opacity(0), colourProvider.Dark2);
|
||||
flashLayer.FadeOutFromOne(800, Easing.OutQuint);
|
||||
}
|
||||
};
|
||||
t.OnInputError = () =>
|
||||
{
|
||||
flashLayer.Colour = ColourInfo.GradientVertical(colours.Red3.Opacity(0), colours.Red3);
|
||||
flashLayer.FadeOutFromOne(200, Easing.OutQuint);
|
||||
};
|
||||
t.OnInputError = () => background.FlashOnInputError();
|
||||
t.TabbableContentContainer = tabbableContentContainer;
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -234,41 +234,6 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString RulesetNotSupportSaving => new TranslatableString(getKey(@"ruleset_not_support_saving"), @"Saving is not supported for this ruleset yet, sorry!");
|
||||
|
||||
/// <summary>
|
||||
/// "Export failed!"
|
||||
/// </summary>
|
||||
public static LocalisableString ExportFailed => new TranslatableString(getKey(@"export_failed"), @"Export failed!");
|
||||
|
||||
/// <summary>
|
||||
/// "Import failed!"
|
||||
/// </summary>
|
||||
public static LocalisableString ImportFailed => new TranslatableString(getKey(@"import_failed"), @"Import failed!");
|
||||
|
||||
/// <summary>
|
||||
/// "Open folder"
|
||||
/// </summary>
|
||||
public static LocalisableString OpenFolder => new TranslatableString(getKey(@"open_folder"), @"Open folder");
|
||||
|
||||
/// <summary>
|
||||
/// "Cleaning up..."
|
||||
/// </summary>
|
||||
public static LocalisableString ExternalEditCleaningUp => new TranslatableString(getKey(@"external_edit_cleaning_up"), @"Cleaning up...");
|
||||
|
||||
/// <summary>
|
||||
/// "Exporting for edit..."
|
||||
/// </summary>
|
||||
public static LocalisableString ExternalEditExporting => new TranslatableString(getKey(@"external_edit_exporting"), @"Exporting for edit...");
|
||||
|
||||
/// <summary>
|
||||
/// "Beatmap is mounted externally"
|
||||
/// </summary>
|
||||
public static LocalisableString BeatmapMountedExternally => new TranslatableString(getKey(@"beatmap_mounted_externally"), @"Beatmap is mounted externally");
|
||||
|
||||
/// <summary>
|
||||
/// "Any changes made to the exported folder will be imported to the game, including file additions, modifications and deletions."
|
||||
/// </summary>
|
||||
public static LocalisableString ExternalEditMountedExplanation => new TranslatableString(getKey(@"external_edit_mounted_explanation"), @"Any changes made to the exported folder will be imported to the game, including file additions, modifications and deletions.");
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,21 +94,6 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString CurrentWorkingLayer => new TranslatableString(getKey(@"current_working_layer"), @"Current working layer");
|
||||
|
||||
/// <summary>
|
||||
/// "Please navigate to a skinnable screen using the scene library"
|
||||
/// </summary>
|
||||
public static LocalisableString NavigateToSkinnableScreen => new TranslatableString(getKey(@"navigate_to_skinnable_screen"), @"Please navigate to a skinnable screen using the scene library");
|
||||
|
||||
/// <summary>
|
||||
/// "Return to game"
|
||||
/// </summary>
|
||||
public static LocalisableString ReturnToGame => new TranslatableString(getKey(@"return_to_game"), @"Return to game");
|
||||
|
||||
/// <summary>
|
||||
/// "Skin is mounted externally"
|
||||
/// </summary>
|
||||
public static LocalisableString SkinMountedExternally => new TranslatableString(getKey(@"skin_mounted_externally"), @"Skin is mounted externally");
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ namespace osu.Game.Overlays.AccountCreation
|
||||
|
||||
apiState.BindValueChanged(state =>
|
||||
{
|
||||
if (this.IsCurrentScreen() && state.NewValue == APIState.RequiresSecondFactorAuth)
|
||||
if (state.NewValue == APIState.RequiresSecondFactorAuth)
|
||||
this.Push(new ScreenEmailVerification());
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
@@ -29,7 +28,6 @@ namespace osu.Game.Overlays.Login
|
||||
private LoadingLayer loading = null!;
|
||||
private FillFlowContainer contentFlow = null!;
|
||||
private OsuTextBox codeTextBox = null!;
|
||||
private readonly IBindable<APIState> apiState = new Bindable<APIState>();
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
@@ -73,43 +71,13 @@ namespace osu.Game.Overlays.Login
|
||||
}
|
||||
};
|
||||
|
||||
updateLastError();
|
||||
|
||||
showContent(api.SessionVerificationMethod!.Value);
|
||||
apiState.BindTo(api.State);
|
||||
}
|
||||
|
||||
private void updateLastError()
|
||||
{
|
||||
if (api.LastLoginError?.Message is string error)
|
||||
{
|
||||
errorText.Alpha = 1;
|
||||
errorText.AddErrors(new[] { error });
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
apiState.BindValueChanged(val =>
|
||||
{
|
||||
// this handles failed verifications.
|
||||
// in the case of failed verifications, `apiState` will briefly change to `Connecting` and then revert to `RequiresSecondFactorAuth`.
|
||||
// the login overlay doesn't need this logic as it will construct a new instance of this screen anyway,
|
||||
// but the *registration* overlay has no such logic and thus needs special handling.
|
||||
if (val.NewValue == APIState.RequiresSecondFactorAuth)
|
||||
{
|
||||
// scheduling required as `APIAccess.State` value can be changed from threads that aren't update
|
||||
// see: `APIAccess.run()` (which is given a dedicated thread) calls `APIAccess.attemptConnect()` which mutates `APIAccess.State`
|
||||
Schedule(() =>
|
||||
{
|
||||
codeTextBox.Current.Disabled = false;
|
||||
codeTextBox.Current.Value = string.Empty;
|
||||
updateLastError();
|
||||
});
|
||||
}
|
||||
});
|
||||
showContent(api.SessionVerificationMethod!.Value);
|
||||
}
|
||||
|
||||
private void showContent(SessionVerificationMethod sessionVerificationMethod)
|
||||
|
||||
@@ -13,7 +13,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Testing;
|
||||
@@ -108,7 +107,7 @@ namespace osu.Game.Overlays.SkinEditor
|
||||
throw new InvalidOperationException("Cannot start multiple concurrent external edits!");
|
||||
|
||||
Show();
|
||||
showSpinner(EditorStrings.ExternalEditExporting);
|
||||
showSpinner("Mounting external skin...");
|
||||
setGlobalSkinDisabled(true);
|
||||
|
||||
await Task.Delay(500).ConfigureAwait(true);
|
||||
@@ -121,7 +120,7 @@ namespace osu.Game.Overlays.SkinEditor
|
||||
{
|
||||
Logger.Log($"Failed to initialize external edit operation: {ex}", LoggingTarget.Database, LogLevel.Error);
|
||||
setGlobalSkinDisabled(false);
|
||||
Schedule(() => showSpinner(EditorStrings.ExportFailed));
|
||||
Schedule(() => showSpinner("Export failed!"));
|
||||
Scheduler.AddDelayed(Hide, 1000);
|
||||
return Task.FromException(ex);
|
||||
}
|
||||
@@ -132,7 +131,7 @@ namespace osu.Game.Overlays.SkinEditor
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = SkinEditorStrings.SkinMountedExternally,
|
||||
Text = "Skin is mounted externally",
|
||||
Font = OsuFont.Default.With(size: 30),
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
@@ -144,11 +143,11 @@ namespace osu.Game.Overlays.SkinEditor
|
||||
Origin = Anchor.TopCentre,
|
||||
Width = 350,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Text = EditorStrings.ExternalEditMountedExplanation,
|
||||
Text = "Any changes made to the exported folder will be imported to the game, including file additions, modifications and deletions.",
|
||||
},
|
||||
new PurpleRoundedButton
|
||||
{
|
||||
Text = EditorStrings.OpenFolder,
|
||||
Text = "Open folder",
|
||||
Width = 350,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
@@ -199,7 +198,7 @@ namespace osu.Game.Overlays.SkinEditor
|
||||
|
||||
Debug.Assert(taskCompletionSource != null);
|
||||
|
||||
showSpinner(EditorStrings.ExternalEditCleaningUp);
|
||||
showSpinner("Cleaning up...");
|
||||
await Task.Delay(500).ConfigureAwait(true);
|
||||
|
||||
try
|
||||
@@ -209,7 +208,7 @@ namespace osu.Game.Overlays.SkinEditor
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log($"Failed to finish external edit operation: {ex}", LoggingTarget.Database, LogLevel.Error);
|
||||
showSpinner(EditorStrings.ImportFailed);
|
||||
showSpinner("Import failed!");
|
||||
Scheduler.AddDelayed(Hide, 1000);
|
||||
setGlobalSkinDisabled(false);
|
||||
taskCompletionSource.SetException(ex);
|
||||
@@ -276,7 +275,7 @@ namespace osu.Game.Overlays.SkinEditor
|
||||
return base.OnPressed(e);
|
||||
}
|
||||
|
||||
private void showSpinner(LocalisableString text)
|
||||
private void showSpinner(string text)
|
||||
{
|
||||
foreach (var b in flow.ChildrenOfType<RoundedButton>())
|
||||
b.Enabled.Value = false;
|
||||
|
||||
@@ -9,7 +9,6 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Localisation;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.SkinEditor
|
||||
@@ -57,7 +56,7 @@ namespace osu.Game.Overlays.SkinEditor
|
||||
TextAnchor = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Text = SkinEditorStrings.NavigateToSkinnableScreen,
|
||||
Text = "Please navigate to a skinnable screen using the scene library",
|
||||
},
|
||||
new RoundedButton
|
||||
{
|
||||
@@ -66,7 +65,7 @@ namespace osu.Game.Overlays.SkinEditor
|
||||
Width = 200,
|
||||
Margin = new MarginPadding { Top = 20 },
|
||||
Action = () => skinEditorOverlay?.Hide(),
|
||||
Text = SkinEditorStrings.ReturnToGame,
|
||||
Text = "Return to game"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -61,10 +61,9 @@ namespace osu.Game.Scoring.Legacy
|
||||
/// <item><description>30000014: Fix edge cases in conversion for osu! scores on selected beatmaps. Reconvert all scores.</description></item>
|
||||
/// <item><description>30000015: Fix osu! standardised score estimation algorithm violating basic invariants. Reconvert all scores.</description></item>
|
||||
/// <item><description>30000016: Fix taiko standardised score estimation algorithm not including swell tick score gain into bonus portion. Reconvert all scores.</description></item>
|
||||
/// <item><description>30000017: Mod score multiplier rebalance. Recalculates the <see cref="ScoreInfo.TotalScore"/> of all scores with <see cref="ScoreInfo.TotalScoreWithoutMods"/> present.</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public const int LATEST_VERSION = 30000017;
|
||||
public const int LATEST_VERSION = 30000016;
|
||||
|
||||
/// <summary>
|
||||
/// The first stable-compatible YYYYMMDD format version given to lazer usage of replays.
|
||||
|
||||
@@ -10,7 +10,6 @@ using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Screens;
|
||||
@@ -119,7 +118,7 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
private async Task begin()
|
||||
{
|
||||
showSpinner(EditorStrings.ExternalEditExporting);
|
||||
showSpinner("Exporting for edit...");
|
||||
|
||||
await Task.Delay(500).ConfigureAwait(true);
|
||||
|
||||
@@ -131,7 +130,7 @@ namespace osu.Game.Screens.Edit
|
||||
{
|
||||
Logger.Log($@"Failed to initiate external edit operation: {ex}", LoggingTarget.Database);
|
||||
fileMountOperation = null;
|
||||
showSpinner(EditorStrings.ExportFailed);
|
||||
showSpinner("Export failed!");
|
||||
await Task.Delay(1000).ConfigureAwait(true);
|
||||
this.Exit();
|
||||
}
|
||||
@@ -140,7 +139,7 @@ namespace osu.Game.Screens.Edit
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = EditorStrings.BeatmapMountedExternally,
|
||||
Text = "Beatmap is mounted externally",
|
||||
Font = OsuFont.Default.With(size: 30),
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
@@ -152,11 +151,11 @@ namespace osu.Game.Screens.Edit
|
||||
Origin = Anchor.TopCentre,
|
||||
Width = 350,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Text = EditorStrings.ExternalEditMountedExplanation,
|
||||
Text = "Any changes made to the exported folder will be imported to the game, including file additions, modifications and deletions.",
|
||||
},
|
||||
new PurpleRoundedButton
|
||||
{
|
||||
Text = EditorStrings.OpenFolder,
|
||||
Text = "Open folder",
|
||||
Width = 350,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
@@ -201,7 +200,7 @@ namespace osu.Game.Screens.Edit
|
||||
BackButtonVisibility.Value = false;
|
||||
string originalDifficulty = editor.Beatmap.Value.Beatmap.BeatmapInfo.DifficultyName;
|
||||
|
||||
showSpinner(EditorStrings.ExternalEditCleaningUp);
|
||||
showSpinner("Cleaning up...");
|
||||
|
||||
Live<BeatmapSetInfo>? beatmap = null;
|
||||
|
||||
@@ -212,7 +211,7 @@ namespace osu.Game.Screens.Edit
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log($@"Failed to finish external edit operation: {ex}", LoggingTarget.Database);
|
||||
showSpinner(EditorStrings.ImportFailed);
|
||||
showSpinner("Import failed!");
|
||||
await Task.Delay(1000).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
@@ -247,7 +246,7 @@ namespace osu.Game.Screens.Edit
|
||||
}
|
||||
}
|
||||
|
||||
private void showSpinner(LocalisableString text)
|
||||
private void showSpinner(string text)
|
||||
{
|
||||
foreach (var b in flow.ChildrenOfType<RoundedButton>())
|
||||
b.Enabled.Value = false;
|
||||
|
||||
@@ -273,9 +273,9 @@ namespace osu.Game.Utils
|
||||
{
|
||||
// Round multiplier values away from 1.00x to two significant digits.
|
||||
if (scoreMultiplier > 1)
|
||||
scoreMultiplier = Math.Ceiling(Math.Round(scoreMultiplier * 100, 4)) / 100;
|
||||
scoreMultiplier = Math.Ceiling(Math.Round(scoreMultiplier * 100, 12)) / 100;
|
||||
else
|
||||
scoreMultiplier = Math.Floor(Math.Round(scoreMultiplier * 100, 4)) / 100;
|
||||
scoreMultiplier = Math.Floor(Math.Round(scoreMultiplier * 100, 12)) / 100;
|
||||
|
||||
return scoreMultiplier.ToLocalisableString("0.00x");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user