mirror of
https://github.com/ppy/osu.git
synced 2026-05-31 16:30:38 +08:00
9314ea94b5
* implement stuff * fix basic issues * rework calculations * sanity check * don't use score based misscount if no scorev1 present * Update OsuPerformanceCalculator.cs * update misscount diff attribute names * add raw score misscount attribute * introduce more reasonable high bound for misscount * code quality changes * Fix osu!catch SR buzz slider detection (#32412) * Use `normalized_hitobject_radius` during osu!catch buzz slider detection Currently the algorithm considers some buzz sliders as standstills when in reality they require movement. This happens because `HalfCatcherWidth` isn't normalized while `exactDistanceMoved` is, leading to an inaccurate comparison. `normalized_hitobject_radius` is the normalized value of `HalfCatcherWidth` and replacing one with the other fixes the problem. * Rename `normalized_hitobject_radius` to `normalized_half_catcher_width` The current name is confusing because hit objects have no radius in the context of osu!catch difficulty calculation. The new name conveys the actual purpose of the value. * Only set `normalized_half_catcher_width` in `CatchDifficultyHitObject` Prevents potential bugs if the value were to be changed in one of the classes but not in both. * Use `CatchDifficultyHitObject.NORMALIZED_HALF_CATCHER_WIDTH` directly Requested during code review. --------- Co-authored-by: James Wilson <tsunyoku@gmail.com> * Move osu!catch movement diffcalc to an evaluator (#32655) * Move osu!catch movement state into `CatchDifficultyHitObject` In order to port `Movement` to an evaluator, the state has to be either moved elsewhere or calculated inside the evaluator. The latter requires backtracking for every hit object, which in the worst case is continued until the beginning of the map is reached. Limiting backtracking can lead to difficulty value changes. Thus, the first option was chosen for its simplicity. * Move osu!catch movement difficulty calculation to an evaluator Makes the code more in line with the other game modes. * Add documentation for `CatchDifficultyHitObject` fields --------- Co-authored-by: James Wilson <tsunyoku@gmail.com> * Move all score-independent bonuses into star rating (#31351) * basis refactor to allow for more complex SR calculations * move all possible bonuses into star rating * decrease star rating scaling to account for overall gains * add extra FL guard for safety * move star rating multiplier into a constant * Reorganise some things * Add HD and SO to difficulty adjustment mods * Move non-legacy mod multipliers back to PP * Some merge fixes * Fix application of flashlight rating multiplier * Fix Hidden bonuses being applied when Blinds mod is in use * Move part of speed OD scaling into difficulty * Move length bonus back to PP * Remove blinds special case * Revert star rating multiplier decrease * More balancing --------- Co-authored-by: StanR <hi@stanr.info> * Add diffcalc considerations for Magnetised mod (#33004) * Add diffcalc considerations for Magnetised mod * Make speed reduction scale with power too * cleaning up * Update OsuPerformanceCalculator.cs * Update OsuPerformanceCalculator.cs * add new check to avoid overestimation * fix code style * fix nvicka * add database attributes * Refactor * Rename `Working` to `WorkingBeatmap` * Remove redundant condition * Remove useless variable * Remove `get` wording * Rename `calculateScoreAtCombo` * Remove redundant operator * Add comments to explain how score-based miss count derivations work * Remove redundant `decimal` calculations * use static method to improve performance * move stuff around for readability * move logic into helper class * fix the bug * Delete OsuLegacyScoreProcessor.cs * Delete ILegacyScoreProcessor.cs * revert static method for multiplier * use only basic combo score attribute * Clean-up * Remove unused param * Update osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs Co-authored-by: StanR <castl@inbox.ru> * rename variables * Add `LegacyScoreUtils` * Add fail safe * Move `countMiss` * Better explain `CalculateRelevantScoreComboPerObject` * Add `OsuLegacyScoreMissCalculator` * Move `CalculateScoreAtCombo` and `CalculateRelevantScoreComboPerObject` * Remove unused variables * Move `GetLegacyScoreMultiplier` * Add `estimated` wording --------- Co-authored-by: wulpine <wulpine@proton.me> Co-authored-by: James Wilson <tsunyoku@gmail.com> Co-authored-by: StanR <hi@stanr.info> Co-authored-by: StanR <castl@inbox.ru>
168 lines
7.5 KiB
C#
168 lines
7.5 KiB
C#
// 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.Collections.Generic;
|
|
using System.Linq;
|
|
using JetBrains.Annotations;
|
|
using Newtonsoft.Json;
|
|
using osu.Game.Beatmaps;
|
|
using osu.Game.Rulesets.Difficulty;
|
|
using osu.Game.Rulesets.Mods;
|
|
using osu.Game.Rulesets.Osu.Objects;
|
|
|
|
namespace osu.Game.Rulesets.Osu.Difficulty
|
|
{
|
|
public class OsuDifficultyAttributes : DifficultyAttributes
|
|
{
|
|
/// <summary>
|
|
/// The difficulty corresponding to the aim skill.
|
|
/// </summary>
|
|
[JsonProperty("aim_difficulty")]
|
|
public double AimDifficulty { get; set; }
|
|
|
|
/// <summary>
|
|
/// The number of <see cref="Slider"/>s weighted by difficulty.
|
|
/// </summary>
|
|
[JsonProperty("aim_difficult_slider_count")]
|
|
public double AimDifficultSliderCount { get; set; }
|
|
|
|
/// <summary>
|
|
/// The difficulty corresponding to the speed skill.
|
|
/// </summary>
|
|
[JsonProperty("speed_difficulty")]
|
|
public double SpeedDifficulty { get; set; }
|
|
|
|
/// <summary>
|
|
/// The number of clickable objects weighted by difficulty.
|
|
/// Related to <see cref="SpeedDifficulty"/>
|
|
/// </summary>
|
|
[JsonProperty("speed_note_count")]
|
|
public double SpeedNoteCount { get; set; }
|
|
|
|
/// <summary>
|
|
/// The difficulty corresponding to the flashlight skill.
|
|
/// </summary>
|
|
[JsonProperty("flashlight_difficulty")]
|
|
public double FlashlightDifficulty { get; set; }
|
|
|
|
/// <summary>
|
|
/// Describes how much of <see cref="AimDifficulty"/> is contributed to by hitcircles or sliders.
|
|
/// A value closer to 1.0 indicates most of <see cref="AimDifficulty"/> is contributed by hitcircles.
|
|
/// A value closer to 0.0 indicates most of <see cref="AimDifficulty"/> is contributed by sliders.
|
|
/// </summary>
|
|
[JsonProperty("slider_factor")]
|
|
public double SliderFactor { get; set; }
|
|
|
|
/// <summary>
|
|
/// Describes how much of <see cref="AimDifficultStrainCount"/> is contributed to by hitcircles or sliders
|
|
/// A value closer to 0.0 indicates most of <see cref="AimDifficultStrainCount"/> is contributed by hitcircles
|
|
/// A value closer to Infinity indicates most of <see cref="AimDifficultStrainCount"/> is contributed by sliders
|
|
/// </summary>
|
|
[JsonProperty("aim_top_weighted_slider_factor")]
|
|
public double AimTopWeightedSliderFactor { get; set; }
|
|
|
|
/// <summary>
|
|
/// Describes how much of <see cref="SpeedDifficultStrainCount"/> is contributed to by hitcircles or sliders
|
|
/// A value closer to 0.0 indicates most of <see cref="SpeedDifficultStrainCount"/> is contributed by hitcircles
|
|
/// A value closer to Infinity indicates most of <see cref="SpeedDifficultStrainCount"/> is contributed by sliders
|
|
/// </summary>
|
|
[JsonProperty("speed_top_weighted_slider_factor")]
|
|
public double SpeedTopWeightedSliderFactor { get; set; }
|
|
|
|
[JsonProperty("aim_difficult_strain_count")]
|
|
public double AimDifficultStrainCount { get; set; }
|
|
|
|
[JsonProperty("speed_difficult_strain_count")]
|
|
public double SpeedDifficultStrainCount { get; set; }
|
|
|
|
[JsonProperty("slider_nested_score_per_object")]
|
|
public double SliderNestedScorePerObject { get; set; }
|
|
|
|
[JsonProperty("legacy_score_base_multiplier")]
|
|
public double LegacyScoreBaseMultiplier { get; set; }
|
|
|
|
[JsonProperty("maximum_legacy_combo_score")]
|
|
public double MaximumLegacyComboScore { get; set; }
|
|
|
|
/// <summary>
|
|
/// The beatmap's drain rate. This doesn't scale with rate-adjusting mods.
|
|
/// </summary>
|
|
public double DrainRate { get; set; }
|
|
|
|
/// <summary>
|
|
/// The number of hitcircles in the beatmap.
|
|
/// </summary>
|
|
public int HitCircleCount { get; set; }
|
|
|
|
/// <summary>
|
|
/// The number of sliders in the beatmap.
|
|
/// </summary>
|
|
public int SliderCount { get; set; }
|
|
|
|
/// <summary>
|
|
/// The number of spinners in the beatmap.
|
|
/// </summary>
|
|
public int SpinnerCount { get; set; }
|
|
|
|
public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes()
|
|
{
|
|
foreach (var v in base.ToDatabaseAttributes())
|
|
yield return v;
|
|
|
|
yield return (ATTRIB_ID_AIM, AimDifficulty);
|
|
yield return (ATTRIB_ID_SPEED, SpeedDifficulty);
|
|
yield return (ATTRIB_ID_DIFFICULTY, StarRating);
|
|
|
|
if (ShouldSerializeFlashlightDifficulty())
|
|
yield return (ATTRIB_ID_FLASHLIGHT, FlashlightDifficulty);
|
|
|
|
yield return (ATTRIB_ID_SLIDER_FACTOR, SliderFactor);
|
|
|
|
yield return (ATTRIB_ID_AIM_DIFFICULT_STRAIN_COUNT, AimDifficultStrainCount);
|
|
yield return (ATTRIB_ID_SPEED_DIFFICULT_STRAIN_COUNT, SpeedDifficultStrainCount);
|
|
yield return (ATTRIB_ID_SPEED_NOTE_COUNT, SpeedNoteCount);
|
|
yield return (ATTRIB_ID_AIM_DIFFICULT_SLIDER_COUNT, AimDifficultSliderCount);
|
|
yield return (ATTRIB_ID_AIM_TOP_WEIGHTED_SLIDER_FACTOR, AimTopWeightedSliderFactor);
|
|
yield return (ATTRIB_ID_SPEED_TOP_WEIGHTED_SLIDER_FACTOR, SpeedTopWeightedSliderFactor);
|
|
yield return (ATTRIB_ID_SLIDER_NESTED_SCORE_PER_OBJECT, SliderNestedScorePerObject);
|
|
yield return (ATTRIB_ID_LEGACY_SCORE_BASE_MULTIPLIER, LegacyScoreBaseMultiplier);
|
|
yield return (ATTRIB_ID_MAXIMUM_LEGACY_COMBO_SCORE, MaximumLegacyComboScore);
|
|
}
|
|
|
|
public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values, IBeatmapOnlineInfo onlineInfo)
|
|
{
|
|
base.FromDatabaseAttributes(values, onlineInfo);
|
|
|
|
AimDifficulty = values[ATTRIB_ID_AIM];
|
|
SpeedDifficulty = values[ATTRIB_ID_SPEED];
|
|
StarRating = values[ATTRIB_ID_DIFFICULTY];
|
|
FlashlightDifficulty = values.GetValueOrDefault(ATTRIB_ID_FLASHLIGHT);
|
|
SliderFactor = values[ATTRIB_ID_SLIDER_FACTOR];
|
|
AimDifficultStrainCount = values[ATTRIB_ID_AIM_DIFFICULT_STRAIN_COUNT];
|
|
SpeedDifficultStrainCount = values[ATTRIB_ID_SPEED_DIFFICULT_STRAIN_COUNT];
|
|
SpeedNoteCount = values[ATTRIB_ID_SPEED_NOTE_COUNT];
|
|
AimDifficultSliderCount = values[ATTRIB_ID_AIM_DIFFICULT_SLIDER_COUNT];
|
|
AimTopWeightedSliderFactor = values[ATTRIB_ID_AIM_TOP_WEIGHTED_SLIDER_FACTOR];
|
|
SpeedTopWeightedSliderFactor = values[ATTRIB_ID_SPEED_TOP_WEIGHTED_SLIDER_FACTOR];
|
|
SliderNestedScorePerObject = values[ATTRIB_ID_SLIDER_NESTED_SCORE_PER_OBJECT];
|
|
LegacyScoreBaseMultiplier = values[ATTRIB_ID_LEGACY_SCORE_BASE_MULTIPLIER];
|
|
MaximumLegacyComboScore = values[ATTRIB_ID_MAXIMUM_LEGACY_COMBO_SCORE];
|
|
DrainRate = onlineInfo.DrainRate;
|
|
HitCircleCount = onlineInfo.CircleCount;
|
|
SliderCount = onlineInfo.SliderCount;
|
|
SpinnerCount = onlineInfo.SpinnerCount;
|
|
}
|
|
|
|
#region Newtonsoft.Json implicit ShouldSerialize() methods
|
|
|
|
// The properties in this region are used implicitly by Newtonsoft.Json to not serialise certain fields in some cases.
|
|
// They rely on being named exactly the same as the corresponding fields (casing included) and as such should NOT be renamed
|
|
// unless the fields are also renamed.
|
|
|
|
[UsedImplicitly]
|
|
public bool ShouldSerializeFlashlightDifficulty() => Mods.Any(m => m is ModFlashlight);
|
|
|
|
#endregion
|
|
}
|
|
}
|