mirror of
https://github.com/osukey/osukey.git
synced 2025-08-05 15:44:04 +09:00
Merge branch 'master' into fix-hitobject-sample-stuck-on-future-seek
This commit is contained in:
@ -7,10 +7,6 @@ namespace osu.Game.Rulesets.Judgements
|
||||
{
|
||||
public class IgnoreJudgement : Judgement
|
||||
{
|
||||
public override bool AffectsCombo => false;
|
||||
|
||||
protected override int NumericResultFor(HitResult result) => 0;
|
||||
|
||||
protected override double HealthIncreaseFor(HitResult result) => 0;
|
||||
public override HitResult MaxResult => HitResult.IgnoreHit;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
@ -11,31 +12,69 @@ 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.
|
||||
/// </summary>
|
||||
protected const double DEFAULT_MAX_HEALTH_INCREASE = 0.05;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this <see cref="Judgement"/> should affect the current combo.
|
||||
/// </summary>
|
||||
[Obsolete("Has no effect. Use HitResult members instead (e.g. use small-tick or bonus to not affect combo).")] // Can be removed 20210328
|
||||
public virtual bool AffectsCombo => true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this <see cref="Judgement"/> should be counted as base (combo) or bonus score.
|
||||
/// </summary>
|
||||
[Obsolete("Has no effect. Use HitResult members instead (e.g. use small-tick or bonus to not affect combo).")] // Can be removed 20210328
|
||||
public virtual bool IsBonus => !AffectsCombo;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum <see cref="HitResult"/> that can be achieved.
|
||||
/// </summary>
|
||||
public virtual HitResult MaxResult => HitResult.Perfect;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this <see cref="Judgement"/> should affect the current combo.
|
||||
/// The minimum <see cref="HitResult"/> that can be achieved - the inverse of <see cref="MaxResult"/>.
|
||||
/// </summary>
|
||||
public virtual bool AffectsCombo => true;
|
||||
public HitResult MinResult
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (MaxResult)
|
||||
{
|
||||
case HitResult.SmallBonus:
|
||||
case HitResult.LargeBonus:
|
||||
case HitResult.IgnoreHit:
|
||||
return HitResult.IgnoreMiss;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this <see cref="Judgement"/> should be counted as base (combo) or bonus score.
|
||||
/// </summary>
|
||||
public virtual bool IsBonus => !AffectsCombo;
|
||||
case HitResult.SmallTickHit:
|
||||
return HitResult.SmallTickMiss;
|
||||
|
||||
case HitResult.LargeTickHit:
|
||||
return HitResult.LargeTickMiss;
|
||||
|
||||
default:
|
||||
return HitResult.Miss;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The numeric score representation for the maximum achievable result.
|
||||
/// </summary>
|
||||
public int MaxNumericResult => NumericResultFor(MaxResult);
|
||||
public int MaxNumericResult => ToNumericResult(MaxResult);
|
||||
|
||||
/// <summary>
|
||||
/// The health increase for the maximum achievable result.
|
||||
@ -47,14 +86,15 @@ namespace osu.Game.Rulesets.Judgements
|
||||
/// </summary>
|
||||
/// <param name="result">The <see cref="HitResult"/> to find the numeric score representation for.</param>
|
||||
/// <returns>The numeric score representation of <paramref name="result"/>.</returns>
|
||||
protected virtual int NumericResultFor(HitResult result) => result > HitResult.Miss ? 1 : 0;
|
||||
[Obsolete("Has no effect. Use ToNumericResult(HitResult) (standardised across all rulesets).")] // Can be made non-virtual 20210328
|
||||
protected virtual int NumericResultFor(HitResult result) => ToNumericResult(result);
|
||||
|
||||
/// <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) => NumericResultFor(result.Type);
|
||||
public int NumericResultFor(JudgementResult result) => ToNumericResult(result.Type);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the numeric health increase of a <see cref="HitResult"/>.
|
||||
@ -65,6 +105,21 @@ namespace osu.Game.Rulesets.Judgements
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
|
||||
case HitResult.SmallTickHit:
|
||||
return DEFAULT_MAX_HEALTH_INCREASE * 0.05;
|
||||
|
||||
case HitResult.SmallTickMiss:
|
||||
return -DEFAULT_MAX_HEALTH_INCREASE * 0.05;
|
||||
|
||||
case HitResult.LargeTickHit:
|
||||
return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
|
||||
|
||||
case HitResult.LargeTickMiss:
|
||||
return -DEFAULT_MAX_HEALTH_INCREASE * 0.1;
|
||||
|
||||
case HitResult.Miss:
|
||||
return -DEFAULT_MAX_HEALTH_INCREASE;
|
||||
|
||||
@ -83,8 +138,11 @@ namespace osu.Game.Rulesets.Judgements
|
||||
case HitResult.Perfect:
|
||||
return DEFAULT_MAX_HEALTH_INCREASE * 1.05;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
case HitResult.SmallBonus:
|
||||
return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
|
||||
|
||||
case HitResult.LargeBonus:
|
||||
return DEFAULT_MAX_HEALTH_INCREASE * 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,6 +153,42 @@ 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() => $"AffectsCombo:{AffectsCombo} MaxResult:{MaxResult} MaxScore:{MaxNumericResult}";
|
||||
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:
|
||||
return 300;
|
||||
|
||||
case HitResult.Perfect:
|
||||
return 350;
|
||||
|
||||
case HitResult.SmallBonus:
|
||||
return SMALL_BONUS_SCORE;
|
||||
|
||||
case HitResult.LargeBonus:
|
||||
return LARGE_BONUS_SCORE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Judgements
|
||||
/// <summary>
|
||||
/// Whether a successful hit occurred.
|
||||
/// </summary>
|
||||
public bool IsHit => Type > HitResult.Miss;
|
||||
public bool IsHit => Type.IsHit();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="JudgementResult"/>.
|
||||
|
@ -16,8 +16,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override string Description => "SS or quit.";
|
||||
|
||||
protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result)
|
||||
=> !(result.Judgement is IgnoreJudgement)
|
||||
&& result.Judgement.AffectsCombo
|
||||
=> result.Type.AffectsAccuracy()
|
||||
&& result.Type != result.Judgement.MaxResult;
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ namespace osu.Game.Rulesets.Mods
|
||||
healthProcessor.FailConditions += FailCondition;
|
||||
}
|
||||
|
||||
protected virtual bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) => !result.IsHit && result.Judgement.AffectsCombo;
|
||||
protected virtual bool FailCondition(HealthProcessor healthProcessor, JudgementResult result)
|
||||
=> result.Type.AffectsCombo()
|
||||
&& !result.IsHit;
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.TypeExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
@ -473,6 +474,30 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
if (!Result.HasResult)
|
||||
throw new InvalidOperationException($"{GetType().ReadableName()} applied a {nameof(JudgementResult)} but did not update {nameof(JudgementResult.Type)}.");
|
||||
|
||||
// Some (especially older) rulesets use scorable judgements instead of the newer ignorehit/ignoremiss judgements.
|
||||
// Can be removed 20210328
|
||||
if (Result.Judgement.MaxResult == HitResult.IgnoreHit)
|
||||
{
|
||||
HitResult originalType = Result.Type;
|
||||
|
||||
if (Result.Type == HitResult.Miss)
|
||||
Result.Type = HitResult.IgnoreMiss;
|
||||
else if (Result.Type >= HitResult.Meh && Result.Type <= HitResult.Perfect)
|
||||
Result.Type = HitResult.IgnoreHit;
|
||||
|
||||
if (Result.Type != originalType)
|
||||
{
|
||||
Logger.Log($"{GetType().ReadableName()} applied an invalid hit result ({originalType}) when {nameof(HitResult.IgnoreMiss)} or {nameof(HitResult.IgnoreHit)} is expected.\n"
|
||||
+ $"This has been automatically adjusted to {Result.Type}, and support will be removed from 2020-03-28 onwards.", level: LogLevel.Important);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Result.Type.IsValidHitResult(Result.Judgement.MinResult, Result.Judgement.MaxResult))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"{GetType().ReadableName()} applied an invalid hit result (was: {Result.Type}, expected: [{Result.Judgement.MinResult} ... {Result.Judgement.MaxResult}]).");
|
||||
}
|
||||
|
||||
// Ensure that the judgement is given a valid time offset, because this may not get set by the caller
|
||||
var endTime = HitObject.GetEndTime();
|
||||
|
||||
|
@ -115,7 +115,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
{
|
||||
base.ApplyResultInternal(result);
|
||||
|
||||
if (!result.Judgement.IsBonus)
|
||||
if (!result.Type.IsBonus())
|
||||
healthIncreases.Add((result.HitObject.GetEndTime() + result.TimeOffset, GetHealthIncreaseFor(result)));
|
||||
}
|
||||
|
||||
|
@ -2,15 +2,19 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Rulesets.Scoring
|
||||
{
|
||||
[HasOrderedElements]
|
||||
public enum HitResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the object has not been judged yet.
|
||||
/// </summary>
|
||||
[Description(@"")]
|
||||
[Order(14)]
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
@ -21,47 +25,175 @@ namespace osu.Game.Rulesets.Scoring
|
||||
/// "too far in the future). It should also define when a forced miss should be triggered (as a result of no user input in time).
|
||||
/// </remarks>
|
||||
[Description(@"Miss")]
|
||||
[Order(5)]
|
||||
Miss,
|
||||
|
||||
[Description(@"Meh")]
|
||||
[Order(4)]
|
||||
Meh,
|
||||
|
||||
/// <summary>
|
||||
/// Optional judgement.
|
||||
/// </summary>
|
||||
[Description(@"OK")]
|
||||
[Order(3)]
|
||||
Ok,
|
||||
|
||||
[Description(@"Good")]
|
||||
[Order(2)]
|
||||
Good,
|
||||
|
||||
[Description(@"Great")]
|
||||
[Order(1)]
|
||||
Great,
|
||||
|
||||
/// <summary>
|
||||
/// Optional judgement.
|
||||
/// </summary>
|
||||
[Description(@"Perfect")]
|
||||
[Order(0)]
|
||||
Perfect,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates small tick miss.
|
||||
/// </summary>
|
||||
[Order(11)]
|
||||
SmallTickMiss,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates a small tick hit.
|
||||
/// </summary>
|
||||
[Description(@"S Tick")]
|
||||
[Order(7)]
|
||||
SmallTickHit,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates a large tick miss.
|
||||
/// </summary>
|
||||
[Order(10)]
|
||||
LargeTickMiss,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates a large tick hit.
|
||||
/// </summary>
|
||||
LargeTickHit
|
||||
[Description(@"L Tick")]
|
||||
[Order(6)]
|
||||
LargeTickHit,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates a small bonus.
|
||||
/// </summary>
|
||||
[Description("S Bonus")]
|
||||
[Order(9)]
|
||||
SmallBonus,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates a large bonus.
|
||||
/// </summary>
|
||||
[Description("L Bonus")]
|
||||
[Order(8)]
|
||||
LargeBonus,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates a miss that should be ignored for scoring purposes.
|
||||
/// </summary>
|
||||
[Order(13)]
|
||||
IgnoreMiss,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates a hit that should be ignored for scoring purposes.
|
||||
/// </summary>
|
||||
[Order(12)]
|
||||
IgnoreHit,
|
||||
}
|
||||
|
||||
public static class HitResultExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether a <see cref="HitResult"/> increases/decreases the combo, and affects the combo portion of the score.
|
||||
/// </summary>
|
||||
public static bool AffectsCombo(this HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case HitResult.Miss:
|
||||
case HitResult.Meh:
|
||||
case HitResult.Ok:
|
||||
case HitResult.Good:
|
||||
case HitResult.Great:
|
||||
case HitResult.Perfect:
|
||||
case HitResult.LargeTickHit:
|
||||
case HitResult.LargeTickMiss:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether a <see cref="HitResult"/> affects the accuracy portion of the score.
|
||||
/// </summary>
|
||||
public static bool AffectsAccuracy(this HitResult result)
|
||||
=> IsScorable(result) && !IsBonus(result);
|
||||
|
||||
/// <summary>
|
||||
/// Whether a <see cref="HitResult"/> should be counted as bonus score.
|
||||
/// </summary>
|
||||
public static bool IsBonus(this HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case HitResult.SmallBonus:
|
||||
case HitResult.LargeBonus:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether a <see cref="HitResult"/> represents a successful hit.
|
||||
/// </summary>
|
||||
public static bool IsHit(this HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case HitResult.None:
|
||||
case HitResult.IgnoreMiss:
|
||||
case HitResult.Miss:
|
||||
case HitResult.SmallTickMiss:
|
||||
case HitResult.LargeTickMiss:
|
||||
return false;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether a <see cref="HitResult"/> is scorable.
|
||||
/// </summary>
|
||||
public static bool IsScorable(this HitResult result) => result >= HitResult.Miss && result < HitResult.IgnoreMiss;
|
||||
|
||||
/// <summary>
|
||||
/// Whether a <see cref="HitResult"/> is valid within a given <see cref="HitResult"/> range.
|
||||
/// </summary>
|
||||
/// <param name="result">The <see cref="HitResult"/> to check.</param>
|
||||
/// <param name="minResult">The minimum <see cref="HitResult"/>.</param>
|
||||
/// <param name="maxResult">The maximum <see cref="HitResult"/>.</param>
|
||||
/// <returns>Whether <see cref="HitResult"/> falls between <paramref name="minResult"/> and <paramref name="maxResult"/>.</returns>
|
||||
public static bool IsValidHitResult(this HitResult result, HitResult minResult, HitResult maxResult)
|
||||
{
|
||||
if (result == HitResult.None)
|
||||
return false;
|
||||
|
||||
if (result == minResult || result == maxResult)
|
||||
return true;
|
||||
|
||||
Debug.Assert(minResult <= maxResult);
|
||||
return result > minResult && result < maxResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,6 @@ namespace osu.Game.Rulesets.Scoring
|
||||
private double maxBaseScore;
|
||||
private double rollingMaxBaseScore;
|
||||
private double baseScore;
|
||||
private double bonusScore;
|
||||
|
||||
private readonly List<HitEvent> hitEvents = new List<HitEvent>();
|
||||
private HitObject lastHitObject;
|
||||
@ -116,14 +115,15 @@ namespace osu.Game.Rulesets.Scoring
|
||||
if (result.FailedAtJudgement)
|
||||
return;
|
||||
|
||||
if (result.Judgement.AffectsCombo)
|
||||
if (!result.Type.IsScorable())
|
||||
return;
|
||||
|
||||
if (result.Type.AffectsCombo())
|
||||
{
|
||||
switch (result.Type)
|
||||
{
|
||||
case HitResult.None:
|
||||
break;
|
||||
|
||||
case HitResult.Miss:
|
||||
case HitResult.LargeTickMiss:
|
||||
Combo.Value = 0;
|
||||
break;
|
||||
|
||||
@ -133,22 +133,16 @@ namespace osu.Game.Rulesets.Scoring
|
||||
}
|
||||
}
|
||||
|
||||
double scoreIncrease = result.Type == HitResult.Miss ? 0 : result.Judgement.NumericResultFor(result);
|
||||
double scoreIncrease = result.Type.IsHit() ? result.Judgement.NumericResultFor(result) : 0;
|
||||
|
||||
if (result.Judgement.IsBonus)
|
||||
if (!result.Type.IsBonus())
|
||||
{
|
||||
if (result.IsHit)
|
||||
bonusScore += scoreIncrease;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.HasResult)
|
||||
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1;
|
||||
|
||||
baseScore += scoreIncrease;
|
||||
rollingMaxBaseScore += result.Judgement.MaxNumericResult;
|
||||
}
|
||||
|
||||
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1;
|
||||
|
||||
hitEvents.Add(CreateHitEvent(result));
|
||||
lastHitObject = result.HitObject;
|
||||
|
||||
@ -171,22 +165,19 @@ namespace osu.Game.Rulesets.Scoring
|
||||
if (result.FailedAtJudgement)
|
||||
return;
|
||||
|
||||
double scoreIncrease = result.Type == HitResult.Miss ? 0 : result.Judgement.NumericResultFor(result);
|
||||
if (!result.Type.IsScorable())
|
||||
return;
|
||||
|
||||
if (result.Judgement.IsBonus)
|
||||
{
|
||||
if (result.IsHit)
|
||||
bonusScore -= scoreIncrease;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.HasResult)
|
||||
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) - 1;
|
||||
double scoreIncrease = result.Type.IsHit() ? result.Judgement.NumericResultFor(result) : 0;
|
||||
|
||||
if (!result.Type.IsBonus())
|
||||
{
|
||||
baseScore -= scoreIncrease;
|
||||
rollingMaxBaseScore -= result.Judgement.MaxNumericResult;
|
||||
}
|
||||
|
||||
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) - 1;
|
||||
|
||||
Debug.Assert(hitEvents.Count > 0);
|
||||
lastHitObject = hitEvents[^1].LastHitObject;
|
||||
hitEvents.RemoveAt(hitEvents.Count - 1);
|
||||
@ -207,7 +198,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
return GetScore(mode, maxHighestCombo,
|
||||
maxBaseScore > 0 ? baseScore / maxBaseScore : 0,
|
||||
maxHighestCombo > 0 ? (double)HighestCombo.Value / maxHighestCombo : 0,
|
||||
bonusScore);
|
||||
scoreResultCounts);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -217,9 +208,9 @@ namespace osu.Game.Rulesets.Scoring
|
||||
/// <param name="maxCombo">The maximum combo achievable in the beatmap.</param>
|
||||
/// <param name="accuracyRatio">The accuracy percentage achieved by the player.</param>
|
||||
/// <param name="comboRatio">The proportion of <paramref name="maxCombo"/> achieved by the player.</param>
|
||||
/// <param name="bonusScore">Any bonus score to be added.</param>
|
||||
/// <param name="statistics">Any statistics to be factored in.</param>
|
||||
/// <returns>The total score.</returns>
|
||||
public double GetScore(ScoringMode mode, int maxCombo, double accuracyRatio, double comboRatio, double bonusScore)
|
||||
public double GetScore(ScoringMode mode, int maxCombo, double accuracyRatio, double comboRatio, Dictionary<HitResult, int> statistics)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
@ -228,14 +219,18 @@ namespace osu.Game.Rulesets.Scoring
|
||||
double accuracyScore = accuracyPortion * accuracyRatio;
|
||||
double comboScore = comboPortion * comboRatio;
|
||||
|
||||
return (max_score * (accuracyScore + comboScore) + bonusScore) * scoreMultiplier;
|
||||
return (max_score * (accuracyScore + comboScore) + getBonusScore(statistics)) * scoreMultiplier;
|
||||
|
||||
case ScoringMode.Classic:
|
||||
// should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1)
|
||||
return bonusScore + (accuracyRatio * maxCombo * 300) * (1 + Math.Max(0, (comboRatio * maxCombo) - 1) * scoreMultiplier / 25);
|
||||
return getBonusScore(statistics) + (accuracyRatio * maxCombo * 300) * (1 + Math.Max(0, (comboRatio * maxCombo) - 1) * scoreMultiplier / 25);
|
||||
}
|
||||
}
|
||||
|
||||
private double getBonusScore(Dictionary<HitResult, int> statistics)
|
||||
=> statistics.GetOrDefault(HitResult.SmallBonus) * Judgement.SMALL_BONUS_SCORE
|
||||
+ statistics.GetOrDefault(HitResult.LargeBonus) * Judgement.LARGE_BONUS_SCORE;
|
||||
|
||||
private ScoreRank rankFrom(double acc)
|
||||
{
|
||||
if (acc == 1)
|
||||
@ -282,7 +277,6 @@ namespace osu.Game.Rulesets.Scoring
|
||||
|
||||
baseScore = 0;
|
||||
rollingMaxBaseScore = 0;
|
||||
bonusScore = 0;
|
||||
|
||||
TotalScore.Value = 0;
|
||||
Accuracy.Value = 1;
|
||||
@ -309,9 +303,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
score.Rank = Rank.Value;
|
||||
score.Date = DateTimeOffset.Now;
|
||||
|
||||
var hitWindows = CreateHitWindows();
|
||||
|
||||
foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r)))
|
||||
foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => r.IsScorable()))
|
||||
score.Statistics[result] = GetStatistic(result);
|
||||
|
||||
score.HitEvents = hitEvents;
|
||||
@ -320,6 +312,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
/// <summary>
|
||||
/// Create a <see cref="HitWindows"/> for this processor.
|
||||
/// </summary>
|
||||
[Obsolete("Method is now unused.")] // Can be removed 20210328
|
||||
public virtual HitWindows CreateHitWindows() => new HitWindows();
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user