1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-29 22:20:34 +08:00

Compare commits

..

8 Commits

34 changed files with 158 additions and 228 deletions
@@ -7,7 +7,7 @@ using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Mods;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Scoring;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
@@ -18,8 +18,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty
{
internal class CatchLegacyScoreSimulator : ILegacyScoreSimulator
{
private readonly ScoreProcessor scoreProcessor = new CatchScoreProcessor();
private int legacyBonusScore;
private int standardisedBonusScore;
private int combo;
@@ -136,7 +134,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
if (isBonus)
{
legacyBonusScore += scoreIncrease;
standardisedBonusScore += scoreProcessor.GetBaseScoreForResult(bonusResult);
standardisedBonusScore += Judgement.ToNumericResult(bonusResult);
}
else
attributes.AccuracyScore += scoreIncrease;
@@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Scoring
}
protected override double GetComboScoreChange(JudgementResult result)
=> GetBaseScoreForResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(combo_cap, combo_base));
=> GetNumericResultFor(result) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(combo_cap, combo_base));
public override ScoreRank RankFromAccuracy(double accuracy)
{
@@ -31,31 +31,44 @@ namespace osu.Game.Rulesets.Mania.Scoring
+ bonusPortion;
}
protected override double GetComboScoreChange(JudgementResult result)
protected override double GetNumericResultFor(JudgementResult result)
{
return getBaseComboScoreForResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base));
}
public override int GetBaseScoreForResult(HitResult result)
{
switch (result)
switch (result.Type)
{
case HitResult.Perfect:
return 305;
}
return base.GetBaseScoreForResult(result);
return base.GetNumericResultFor(result);
}
private int getBaseComboScoreForResult(HitResult result)
protected override double GetMaxNumericResultFor(JudgementResult result)
{
switch (result)
switch (result.Judgement.MaxResult)
{
case HitResult.Perfect:
return 300;
return 305;
}
return GetBaseScoreForResult(result);
return base.GetMaxNumericResultFor(result);
}
protected override double GetComboScoreChange(JudgementResult result)
{
double numericResult;
switch (result.Type)
{
case HitResult.Perfect:
numericResult = 300;
break;
default:
numericResult = GetNumericResultFor(result);
break;
}
return numericResult * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base));
}
private class JudgementOrderComparer : IComparer<HitObject>
@@ -58,7 +58,10 @@ namespace osu.Game.Rulesets.Osu.Tests
double trackerRotationTolerance = 0;
addSeekStep(5000);
AddStep("calculate rotation tolerance", () => { trackerRotationTolerance = Math.Abs(drawableSpinner.RotationTracker.Rotation * 0.1f); });
AddStep("calculate rotation tolerance", () =>
{
trackerRotationTolerance = Math.Abs(drawableSpinner.RotationTracker.Rotation * 0.1f);
});
AddAssert("is disc rotation not almost 0", () => drawableSpinner.RotationTracker.Rotation, () => Is.Not.EqualTo(0).Within(100));
AddAssert("is disc rotation absolute not almost 0", () => drawableSpinner.Result.TotalRotation, () => Is.Not.EqualTo(0).Within(100));
@@ -130,11 +133,9 @@ namespace osu.Game.Rulesets.Osu.Tests
AddAssert("player score matching expected bonus score", () =>
{
var scoreProcessor = ((ScoreExposedPlayer)Player).ScoreProcessor;
// multipled by 2 to nullify the score multiplier. (autoplay mod selected)
long totalScore = scoreProcessor.TotalScore.Value * 2;
return totalScore == (int)(drawableSpinner.Result.TotalRotation / 360) * scoreProcessor.GetBaseScoreForResult(new SpinnerTick().CreateJudgement().MaxResult);
long totalScore = ((ScoreExposedPlayer)Player).ScoreProcessor.TotalScore.Value * 2;
return totalScore == (int)(drawableSpinner.Result.TotalRotation / 360) * new SpinnerTick().CreateJudgement().MaxNumericResult;
});
addSeekStep(0);
@@ -5,12 +5,12 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Scoring.Legacy;
@@ -18,8 +18,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
internal class OsuLegacyScoreSimulator : ILegacyScoreSimulator
{
private readonly ScoreProcessor scoreProcessor = new OsuScoreProcessor();
private int legacyBonusScore;
private int standardisedBonusScore;
private int combo;
@@ -173,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (isBonus)
{
legacyBonusScore += scoreIncrease;
standardisedBonusScore += scoreProcessor.GetBaseScoreForResult(bonusResult);
standardisedBonusScore += Judgement.ToNumericResult(bonusResult);
}
else
attributes.AccuracyScore += scoreIncrease;
@@ -1,11 +1,14 @@
// 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 System.Linq;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModAccuracyChallenge : ModAccuracyChallenge
{
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray();
}
}
@@ -29,6 +29,8 @@ namespace osu.Game.Rulesets.Osu.Mods
{
typeof(OsuModSpunOut),
typeof(ModRelax),
typeof(ModFailCondition),
typeof(ModNoFail),
typeof(ModAutoplay),
typeof(OsuModMagnetised),
typeof(OsuModRepel),
@@ -1,11 +1,14 @@
// 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 System.Linq;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModNoFail : ModNoFail
{
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray();
}
}
@@ -1,11 +1,14 @@
// 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 System.Linq;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModPerfect : ModPerfect
{
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
}
}
@@ -11,6 +11,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[]
{
typeof(OsuModAutopilot),
typeof(OsuModTargetPractice),
}).ToArray();
}
@@ -17,7 +17,6 @@ using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Rulesets.Scoring;
@@ -313,7 +312,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
updateBonusScore();
}
private static readonly int score_per_tick = new OsuScoreProcessor().GetBaseScoreForResult(new SpinnerBonusTick.OsuSpinnerBonusTickJudgement().MaxResult);
private static readonly int score_per_tick = new SpinnerBonusTick.OsuSpinnerBonusTickJudgement().MaxNumericResult;
private void updateBonusScore()
{
@@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
{
case GameplaySkinComponentLookup<HitResult> resultComponent:
// This should eventually be moved to a skin setting, when supported.
if (Skin is ArgonProSkin && (resultComponent.Component == HitResult.Great || resultComponent.Component == HitResult.Perfect))
if (Skin is ArgonProSkin && resultComponent.Component >= HitResult.Great)
return Drawable.Empty();
return new ArgonJudgementPiece(resultComponent.Component);
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
@@ -12,14 +13,11 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Scoring.Legacy;
using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Scoring;
namespace osu.Game.Rulesets.Taiko.Difficulty
{
internal class TaikoLegacyScoreSimulator : ILegacyScoreSimulator
{
private readonly ScoreProcessor scoreProcessor = new TaikoScoreProcessor();
private int legacyBonusScore;
private int standardisedBonusScore;
private int combo;
@@ -193,7 +191,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (isBonus)
{
legacyBonusScore += scoreIncrease;
standardisedBonusScore += scoreProcessor.GetBaseScoreForResult(bonusResult);
standardisedBonusScore += Judgement.ToNumericResult(bonusResult);
}
else
attributes.AccuracyScore += scoreIncrease;
@@ -28,20 +28,20 @@ namespace osu.Game.Rulesets.Taiko.Scoring
protected override double GetComboScoreChange(JudgementResult result)
{
return GetBaseScoreForResult(result.Type)
return GetNumericResultFor(result)
* Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base))
* strongScaleValue(result);
}
public override int GetBaseScoreForResult(HitResult result)
protected override double GetNumericResultFor(JudgementResult result)
{
switch (result)
switch (result.Type)
{
case HitResult.Ok:
return 150;
}
return base.GetBaseScoreForResult(result);
return base.GetNumericResultFor(result);
}
private double strongScaleValue(JudgementResult result)
@@ -219,8 +219,6 @@ namespace osu.Game.Tests.Beatmaps.Formats
{
new OsuModDoubleTime { SpeedChange = { Value = 1.1 } }
};
scoreInfo.OnlineID = 123123;
scoreInfo.ClientVersion = "2023.1221.0";
var beatmap = new TestBeatmap(ruleset);
var score = new Score
@@ -239,11 +237,9 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.Multiple(() =>
{
Assert.That(decodedAfterEncode.ScoreInfo.OnlineID, Is.EqualTo(123123));
Assert.That(decodedAfterEncode.ScoreInfo.Statistics, Is.EqualTo(scoreInfo.Statistics));
Assert.That(decodedAfterEncode.ScoreInfo.MaximumStatistics, Is.EqualTo(scoreInfo.MaximumStatistics));
Assert.That(decodedAfterEncode.ScoreInfo.Mods, Is.EqualTo(scoreInfo.Mods));
Assert.That(decodedAfterEncode.ScoreInfo.ClientVersion, Is.EqualTo("2023.1221.0"));
});
}
@@ -48,7 +48,7 @@ namespace osu.Game.Tests.Gameplay
// Apply a judgement
scoreProcessor.ApplyResult(new JudgementResult(new HitObject(), new TestJudgement(HitResult.LargeBonus)) { Type = HitResult.LargeBonus });
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(scoreProcessor.GetBaseScoreForResult(HitResult.LargeBonus)));
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(Judgement.LARGE_BONUS_SCORE));
}
[Test]
@@ -196,7 +196,6 @@ namespace osu.Game.Tests.Scores.IO
User = new APIUser { Username = "Test user" },
BeatmapInfo = beatmap.Beatmaps.First(),
Ruleset = new OsuRuleset().RulesetInfo,
ClientVersion = "12345",
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
};
@@ -204,7 +203,6 @@ namespace osu.Game.Tests.Scores.IO
Assert.IsTrue(imported.Mods.Any(m => m is OsuModHardRock));
Assert.IsTrue(imported.Mods.Any(m => m is OsuModDoubleTime));
Assert.That(imported.ClientVersion, Is.EqualTo(toImport.ClientVersion));
}
finally
{
@@ -41,9 +41,6 @@ namespace osu.Game.Tests.Visual.Gameplay
private BeatmapSetInfo? importedSet;
[Resolved]
private OsuGameBase osu { get; set; } = null!;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
{
@@ -156,7 +153,6 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("results displayed", () => Player.GetChildScreen() is ResultsScreen);
AddUntilStep("score in database", () => Realm.Run(r => r.Find<ScoreInfo>(Player.Score.ScoreInfo.ID) != null));
AddUntilStep("score has correct version", () => Realm.Run(r => r.Find<ScoreInfo>(Player.Score.ScoreInfo.ID)!.ClientVersion), () => Is.EqualTo(osu.Version));
}
[Test]
@@ -420,7 +420,7 @@ namespace osu.Game.Tests.Visual.SongSelect
}
[Test]
[Ignore("temporary while peppy investigates. probably realm batching related.")]
[FlakyTest] // temporary while peppy investigates
public void TestSelectionRetainedOnBeatmapUpdate()
{
createSongSelect();
+4 -1
View File
@@ -340,12 +340,15 @@ namespace osu.Game
try
{
var score = scoreManager.Query(s => s.ID == id);
long newTotalScore = StandardisedScoreMigrationTools.ConvertFromLegacyTotalScore(score, beatmapManager);
// Can't use async overload because we're not on the update thread.
// ReSharper disable once MethodHasAsyncOverload
realmAccess.Write(r =>
{
ScoreInfo s = r.Find<ScoreInfo>(id)!;
StandardisedScoreMigrationTools.UpdateFromLegacy(s, beatmapManager);
s.TotalScore = newTotalScore;
s.TotalScoreVersion = LegacyScoreEncoder.LATEST_VERSION;
});
+1 -2
View File
@@ -90,9 +90,8 @@ namespace osu.Game.Database
/// 36 2023-10-26 Add LegacyOnlineID to ScoreInfo. Move osu_scores_*_high IDs stored in OnlineID to LegacyOnlineID. Reset anomalous OnlineIDs.
/// 38 2023-12-10 Add EndTimeObjectCount and TotalObjectCount to BeatmapInfo.
/// 39 2023-12-19 Migrate any EndTimeObjectCount and TotalObjectCount values of 0 to -1 to better identify non-calculated values.
/// 40 2023-12-21 Add ScoreInfo.Version to keep track of which build scores were set on.
/// </summary>
private const int schema_version = 40;
private const int schema_version = 39;
/// <summary>
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
@@ -57,14 +57,14 @@ namespace osu.Game.Database
// We are constructing a "best possible" score from the statistics provided because it's the best we can do.
List<HitResult> sortedHits = score.Statistics
.Where(kvp => kvp.Key.AffectsCombo())
.OrderByDescending(kvp => processor.GetBaseScoreForResult(kvp.Key))
.OrderByDescending(kvp => Judgement.ToNumericResult(kvp.Key))
.SelectMany(kvp => Enumerable.Repeat(kvp.Key, kvp.Value))
.ToList();
// Attempt to use maximum statistics from the database.
var maximumJudgements = score.MaximumStatistics
.Where(kvp => kvp.Key.AffectsCombo())
.OrderByDescending(kvp => processor.GetBaseScoreForResult(kvp.Key))
.OrderByDescending(kvp => Judgement.ToNumericResult(kvp.Key))
.SelectMany(kvp => Enumerable.Repeat(new FakeJudgement(kvp.Key), kvp.Value))
.ToList();
@@ -169,10 +169,10 @@ namespace osu.Game.Database
public static long GetOldStandardised(ScoreInfo score)
{
double accuracyScore =
(double)score.Statistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => numericScoreFor(kvp.Key) * kvp.Value)
/ score.MaximumStatistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => numericScoreFor(kvp.Key) * kvp.Value);
(double)score.Statistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value)
/ score.MaximumStatistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value);
double comboScore = (double)score.MaxCombo / score.MaximumStatistics.Where(kvp => kvp.Key.AffectsCombo()).Sum(kvp => kvp.Value);
double bonusScore = score.Statistics.Where(kvp => kvp.Key.IsBonus()).Sum(kvp => numericScoreFor(kvp.Key) * kvp.Value);
double bonusScore = score.Statistics.Where(kvp => kvp.Key.IsBonus()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value);
double accuracyPortion = 0.3;
@@ -193,65 +193,6 @@ namespace osu.Game.Database
modMultiplier *= mod.ScoreMultiplier;
return (long)Math.Round((1000000 * (accuracyPortion * accuracyScore + (1 - accuracyPortion) * comboScore) + bonusScore) * modMultiplier);
static int numericScoreFor(HitResult result)
{
switch (result)
{
default:
return 0;
case HitResult.SmallTickHit:
return 10;
case HitResult.LargeTickHit:
return 30;
case HitResult.Meh:
return 50;
case HitResult.Ok:
return 100;
case HitResult.Good:
return 200;
case HitResult.Great:
return 300;
case HitResult.Perfect:
return 315;
case HitResult.SmallBonus:
return 10;
case HitResult.LargeBonus:
return 50;
}
}
}
/// <summary>
/// Updates a legacy <see cref="ScoreInfo"/> to standardised scoring.
/// </summary>
/// <param name="score">The score to update.</param>
/// <param name="beatmaps">A <see cref="BeatmapManager"/> used for <see cref="WorkingBeatmap"/> lookups.</param>
public static void UpdateFromLegacy(ScoreInfo score, BeatmapManager beatmaps)
{
score.TotalScore = convertFromLegacyTotalScore(score, beatmaps);
score.Accuracy = ComputeAccuracy(score);
}
/// <summary>
/// Updates a legacy <see cref="ScoreInfo"/> to standardised scoring.
/// </summary>
/// <param name="score">The score to update.</param>
/// <param name="difficulty">The beatmap difficulty.</param>
/// <param name="attributes">The legacy scoring attributes for the beatmap which the score was set on.</param>
public static void UpdateFromLegacy(ScoreInfo score, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes)
{
score.TotalScore = convertFromLegacyTotalScore(score, difficulty, attributes);
score.Accuracy = ComputeAccuracy(score);
}
/// <summary>
@@ -260,7 +201,7 @@ namespace osu.Game.Database
/// <param name="score">The score to convert the total score of.</param>
/// <param name="beatmaps">A <see cref="BeatmapManager"/> used for <see cref="WorkingBeatmap"/> lookups.</param>
/// <returns>The standardised total score.</returns>
private static long convertFromLegacyTotalScore(ScoreInfo score, BeatmapManager beatmaps)
public static long ConvertFromLegacyTotalScore(ScoreInfo score, BeatmapManager beatmaps)
{
if (!score.IsLegacyScore)
return score.TotalScore;
@@ -283,7 +224,7 @@ namespace osu.Game.Database
ILegacyScoreSimulator sv1Simulator = legacyRuleset.CreateLegacyScoreSimulator();
LegacyScoreAttributes attributes = sv1Simulator.Simulate(beatmap, playableBeatmap);
return convertFromLegacyTotalScore(score, LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap.Beatmap), attributes);
return ConvertFromLegacyTotalScore(score, LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap.Beatmap), attributes);
}
/// <summary>
@@ -293,7 +234,7 @@ namespace osu.Game.Database
/// <param name="difficulty">The beatmap difficulty.</param>
/// <param name="attributes">The legacy scoring attributes for the beatmap which the score was set on.</param>
/// <returns>The standardised total score.</returns>
private static long convertFromLegacyTotalScore(ScoreInfo score, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes)
public static long ConvertFromLegacyTotalScore(ScoreInfo score, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes)
{
if (!score.IsLegacyScore)
return score.TotalScore;
@@ -445,19 +386,6 @@ namespace osu.Game.Database
}
}
public static double ComputeAccuracy(ScoreInfo scoreInfo)
{
Ruleset ruleset = scoreInfo.Ruleset.CreateInstance();
ScoreProcessor scoreProcessor = ruleset.CreateScoreProcessor();
int baseScore = scoreInfo.Statistics.Where(kvp => kvp.Key.AffectsAccuracy())
.Sum(kvp => kvp.Value * scoreProcessor.GetBaseScoreForResult(kvp.Key));
int maxBaseScore = scoreInfo.MaximumStatistics.Where(kvp => kvp.Key.AffectsAccuracy())
.Sum(kvp => kvp.Value * scoreProcessor.GetBaseScoreForResult(kvp.Key));
return maxBaseScore == 0 ? 1 : baseScore / (double)maxBaseScore;
}
/// <summary>
/// Used to populate the <paramref name="score"/> model using data parsed from its corresponding replay file.
/// </summary>
+58 -1
View File
@@ -11,6 +11,16 @@ namespace osu.Game.Rulesets.Judgements
/// </summary>
public class Judgement
{
/// <summary>
/// The score awarded for a small bonus.
/// </summary>
public const int SMALL_BONUS_SCORE = 10;
/// <summary>
/// The score awarded for a large bonus.
/// </summary>
public const int LARGE_BONUS_SCORE = 50;
/// <summary>
/// The default health increase for a maximum judgement, as a proportion of total health.
/// By default, each maximum judgement restores 5% of total health.
@@ -81,11 +91,23 @@ namespace osu.Game.Rulesets.Judgements
}
}
/// <summary>
/// The numeric score representation for the maximum achievable result.
/// </summary>
public int MaxNumericResult => ToNumericResult(MaxResult);
/// <summary>
/// The health increase for the maximum achievable result.
/// </summary>
public double MaxHealthIncrease => HealthIncreaseFor(MaxResult);
/// <summary>
/// Retrieves the numeric score representation of a <see cref="JudgementResult"/>.
/// </summary>
/// <param name="result">The <see cref="JudgementResult"/> to find the numeric score representation for.</param>
/// <returns>The numeric score representation of <paramref name="result"/>.</returns>
public int NumericResultFor(JudgementResult result) => ToNumericResult(result.Type);
/// <summary>
/// Retrieves the numeric health increase of a <see cref="HitResult"/>.
/// </summary>
@@ -143,6 +165,41 @@ namespace osu.Game.Rulesets.Judgements
/// <returns>The numeric health increase of <paramref name="result"/>.</returns>
public double HealthIncreaseFor(JudgementResult result) => HealthIncreaseFor(result.Type);
public override string ToString() => $"MaxResult:{MaxResult}";
public override string ToString() => $"MaxResult:{MaxResult} MaxScore:{MaxNumericResult}";
public static int ToNumericResult(HitResult result)
{
switch (result)
{
default:
return 0;
case HitResult.SmallTickHit:
return 10;
case HitResult.LargeTickHit:
return 30;
case HitResult.Meh:
return 50;
case HitResult.Ok:
return 100;
case HitResult.Good:
return 200;
case HitResult.Great:
// Perfect doesn't actually give more score / accuracy directly.
case HitResult.Perfect:
return 300;
case HitResult.SmallBonus:
return SMALL_BONUS_SCORE;
case HitResult.LargeBonus:
return LARGE_BONUS_SCORE;
}
}
}
}
@@ -112,6 +112,6 @@ namespace osu.Game.Rulesets.Judgements
RawTime = null;
}
public override string ToString() => $"{Type} ({Judgement})";
public override string ToString() => $"{Type} (Score:{Judgement.NumericResultFor(this)} HP:{Judgement.HealthIncreaseFor(this)} {Judgement})";
}
}
-6
View File
@@ -272,9 +272,6 @@ namespace osu.Game.Rulesets.Scoring
/// <summary>
/// Whether a <see cref="HitResult"/> represents a miss of any type.
/// </summary>
/// <remarks>
/// Of note, both <see cref="IsMiss"/> and <see cref="IsHit"/> return <see langword="false"/> for <see cref="HitResult.None"/>.
/// </remarks>
public static bool IsMiss(this HitResult result)
{
switch (result)
@@ -294,9 +291,6 @@ namespace osu.Game.Rulesets.Scoring
/// <summary>
/// Whether a <see cref="HitResult"/> represents a successful hit.
/// </summary>
/// <remarks>
/// Of note, both <see cref="IsMiss"/> and <see cref="IsHit"/> return <see langword="false"/> for <see cref="HitResult.None"/>.
/// </remarks>
public static bool IsHit(this HitResult result)
{
switch (result)
+16 -46
View File
@@ -227,12 +227,12 @@ namespace osu.Game.Rulesets.Scoring
if (result.Judgement.MaxResult.AffectsAccuracy())
{
currentMaximumBaseScore += GetBaseScoreForResult(result.Judgement.MaxResult);
currentMaximumBaseScore += GetMaxNumericResultFor(result);
currentAccuracyJudgementCount++;
}
if (result.Type.AffectsAccuracy())
currentBaseScore += GetBaseScoreForResult(result.Type);
currentBaseScore += GetNumericResultFor(result);
if (result.Type.IsBonus())
currentBonusPortion += GetBonusScoreChange(result);
@@ -276,12 +276,12 @@ namespace osu.Game.Rulesets.Scoring
if (result.Judgement.MaxResult.AffectsAccuracy())
{
currentMaximumBaseScore -= GetBaseScoreForResult(result.Judgement.MaxResult);
currentMaximumBaseScore -= GetMaxNumericResultFor(result);
currentAccuracyJudgementCount--;
}
if (result.Type.AffectsAccuracy())
currentBaseScore -= GetBaseScoreForResult(result.Type);
currentBaseScore -= GetNumericResultFor(result);
if (result.Type.IsBonus())
currentBonusPortion -= GetBonusScoreChange(result);
@@ -297,51 +297,21 @@ namespace osu.Game.Rulesets.Scoring
updateScore();
}
/// <summary>
/// Gets the final score change to be applied to the bonus portion of the score.
/// </summary>
/// <param name="result">The judgement result.</param>
protected virtual double GetBonusScoreChange(JudgementResult result) => GetBaseScoreForResult(result.Type);
protected virtual double GetBonusScoreChange(JudgementResult result) => GetNumericResultFor(result);
protected virtual double GetComboScoreChange(JudgementResult result) => GetMaxNumericResultFor(result) * Math.Pow(result.ComboAfterJudgement, COMBO_EXPONENT);
/// <summary>
/// Gets the final score change to be applied to the combo portion of the score.
/// Retrieves the numeric score representation for a <see cref="JudgementResult"/>.
/// </summary>
/// <param name="result">The judgement result.</param>
protected virtual double GetComboScoreChange(JudgementResult result) => GetBaseScoreForResult(result.Judgement.MaxResult) * Math.Pow(result.ComboAfterJudgement, COMBO_EXPONENT);
/// <param name="result">The <see cref="JudgementResult"/>.</param>
protected virtual double GetNumericResultFor(JudgementResult result) => result.Judgement.NumericResultFor(result);
public virtual int GetBaseScoreForResult(HitResult result)
{
switch (result)
{
default:
return 0;
case HitResult.SmallTickHit:
return 10;
case HitResult.LargeTickHit:
return 30;
case HitResult.Meh:
return 50;
case HitResult.Ok:
return 100;
case HitResult.Good:
return 200;
case HitResult.Great:
case HitResult.Perfect: // Perfect doesn't actually give more score / accuracy directly.
return 300;
case HitResult.SmallBonus:
return 10;
case HitResult.LargeBonus:
return 50;
}
}
/// <summary>
/// Retrieves the maximum numeric score representation for a <see cref="JudgementResult"/>.
/// </summary>
/// <param name="result">The <see cref="JudgementResult"/>.</param>
protected virtual double GetMaxNumericResultFor(JudgementResult result) => result.Judgement.MaxNumericResult;
protected virtual void ApplyScoreChange(JudgementResult result)
{
@@ -570,7 +540,7 @@ namespace osu.Game.Rulesets.Scoring
/// </summary>
/// <remarks>
/// Used to compute accuracy.
/// See: <see cref="HitResultExtensions.IsBasic"/> and <see cref="ScoreProcessor.GetBaseScoreForResult"/>.
/// See: <see cref="HitResultExtensions.IsBasic"/> and <see cref="Judgement.ToNumericResult"/>.
/// </remarks>
[Key(0)]
public double BaseScore { get; set; }
@@ -35,16 +35,12 @@ namespace osu.Game.Scoring.Legacy
[JsonProperty("maximum_statistics")]
public Dictionary<HitResult, int> MaximumStatistics { get; set; } = new Dictionary<HitResult, int>();
[JsonProperty("client_version")]
public string ClientVersion = string.Empty;
public static LegacyReplaySoloScoreInfo FromScore(ScoreInfo score) => new LegacyReplaySoloScoreInfo
{
OnlineID = score.OnlineID,
Mods = score.APIMods,
Statistics = score.Statistics.Where(kvp => kvp.Value != 0).ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
MaximumStatistics = score.MaximumStatistics.Where(kvp => kvp.Value != 0).ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
ClientVersion = score.ClientVersion,
};
}
}
@@ -125,7 +125,6 @@ namespace osu.Game.Scoring.Legacy
score.ScoreInfo.Statistics = readScore.Statistics;
score.ScoreInfo.MaximumStatistics = readScore.MaximumStatistics;
score.ScoreInfo.Mods = readScore.Mods.Select(m => m.ToMod(currentRuleset)).ToArray();
score.ScoreInfo.ClientVersion = readScore.ClientVersion;
});
}
}
@@ -34,10 +34,9 @@ namespace osu.Game.Scoring.Legacy
/// <item><description>30000005: Introduce combo exponent in the osu! gamemode. Reconvert all scores.</description></item>
/// <item><description>30000006: Fix edge cases in conversion after combo exponent introduction that lead to NaNs. Reconvert all scores.</description></item>
/// <item><description>30000007: Adjust osu!mania combo and accuracy portions and judgement scoring values. Reconvert all scores.</description></item>
/// <item><description>30000008: Add accuracy conversion. Reconvert all scores.</description></item>
/// </list>
/// </remarks>
public const int LATEST_VERSION = 30000008;
public const int LATEST_VERSION = 30000007;
/// <summary>
/// The first stable-compatible YYYYMMDD format version given to lazer usage of replays.
+3 -3
View File
@@ -17,6 +17,7 @@ using osu.Game.Scoring.Legacy;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using Realms;
@@ -106,7 +107,7 @@ namespace osu.Game.Scoring
else if (model.IsLegacyScore)
{
model.LegacyTotalScore = model.TotalScore;
StandardisedScoreMigrationTools.UpdateFromLegacy(model, beatmaps());
model.TotalScore = StandardisedScoreMigrationTools.ConvertFromLegacyTotalScore(model, beatmaps());
}
}
@@ -124,14 +125,13 @@ namespace osu.Game.Scoring
var beatmap = score.BeatmapInfo!.Detach();
var ruleset = score.Ruleset.Detach();
var rulesetInstance = ruleset.CreateInstance();
var scoreProcessor = rulesetInstance.CreateScoreProcessor();
Debug.Assert(rulesetInstance != null);
// Populate the maximum statistics.
HitResult maxBasicResult = rulesetInstance.GetHitResults()
.Select(h => h.result)
.Where(h => h.IsBasic()).MaxBy(scoreProcessor.GetBaseScoreForResult);
.Where(h => h.IsBasic()).MaxBy(Judgement.ToNumericResult);
foreach ((HitResult result, int count) in score.Statistics)
{
-6
View File
@@ -46,12 +46,6 @@ namespace osu.Game.Scoring
/// </remarks>
public BeatmapInfo? BeatmapInfo { get; set; }
/// <summary>
/// The version of the client this score was set using.
/// Sourced from <see cref="OsuGameBase.Version"/> at the point of score submission.
/// </summary>
public string ClientVersion { get; set; } = string.Empty;
/// <summary>
/// The <see cref="osu.Game.Beatmaps.BeatmapInfo.Hash"/> at the point in time when the score was set.
/// </summary>
+1 -8
View File
@@ -109,9 +109,6 @@ namespace osu.Game.Screens.Play
[Resolved]
private MusicController musicController { get; set; }
[Resolved]
private OsuGameBase game { get; set; }
public GameplayState GameplayState { get; private set; }
private Ruleset ruleset;
@@ -1158,11 +1155,7 @@ namespace osu.Game.Screens.Play
/// <returns>The <see cref="Scoring.Score"/>.</returns>
protected virtual Score CreateScore(IBeatmap beatmap) => new Score
{
ScoreInfo = new ScoreInfo
{
User = api.LocalUser.Value,
ClientVersion = game.Version,
},
ScoreInfo = new ScoreInfo { User = api.LocalUser.Value },
};
/// <summary>
@@ -102,9 +102,6 @@ namespace osu.Game.Screens.Play
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{
if (e.Repeat)
return false;
switch (e.Action)
{
case GlobalAction.SaveReplay:
+7 -15
View File
@@ -50,16 +50,17 @@ namespace osu.Game.Skinning
// legacy judgements don't play any transforms if they are an animation.... UNLESS they are the temporary displayed judgement from new piece.
if (animation?.FrameCount > 1 && !forceTransforms)
{
if (isMissedTick())
applyMissedTickScaling();
return;
}
if (result.IsMiss())
{
if (isMissedTick())
applyMissedTickScaling();
bool isTick = result != HitResult.Miss;
if (isTick)
{
this.ScaleTo(0.6f);
this.ScaleTo(0.3f, 100, Easing.In);
}
else
{
this.ScaleTo(1.6f);
@@ -86,7 +87,6 @@ namespace osu.Game.Skinning
.ScaleTo(1.1f, fade_in_length * 0.8f).Then() // t = 0.8
.Delay(fade_in_length * 0.2f) // t = 1.0
.ScaleTo(0.9f, fade_in_length * 0.2f).Then() // t = 1.2
// stable dictates scale of 0.9->1 over time 1.0 to 1.4, but we are already at 1.2.
// so we need to force the current value to be correct at 1.2 (0.95) then complete the
// second half of the transform.
@@ -94,14 +94,6 @@ namespace osu.Game.Skinning
}
}
private bool isMissedTick() => result.IsMiss() && result != HitResult.Miss;
private void applyMissedTickScaling()
{
this.ScaleTo(0.6f);
this.ScaleTo(0.3f, 100, Easing.In);
}
public Drawable GetAboveHitObjectsProxiedContent() => CreateProxy();
}
}