2019-01-24 16:43:03 +08:00
// 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.
2018-05-17 16:40:46 +08:00
2022-06-17 15:37:17 +08:00
#nullable disable
2018-05-17 16:40:46 +08:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using osu.Game.Rulesets.Difficulty ;
using osu.Game.Rulesets.Mods ;
using osu.Game.Rulesets.Scoring ;
2018-11-12 01:38:12 +08:00
using osu.Game.Rulesets.Taiko.Objects ;
2018-11-28 15:12:57 +08:00
using osu.Game.Scoring ;
2018-05-17 16:40:46 +08:00
namespace osu.Game.Rulesets.Taiko.Difficulty
{
public class TaikoPerformanceCalculator : PerformanceCalculator
{
private int countGreat ;
2020-09-29 16:16:55 +08:00
private int countOk ;
2018-05-17 16:40:46 +08:00
private int countMeh ;
private int countMiss ;
2022-10-05 19:21:15 +08:00
private double accuracy ;
2018-05-17 16:40:46 +08:00
2022-08-19 20:57:28 +08:00
private double effectiveMissCount ;
2022-03-15 11:37:39 +08:00
public TaikoPerformanceCalculator ( )
: base ( new TaikoRuleset ( ) )
2018-05-17 16:40:46 +08:00
{
}
2022-03-14 13:25:26 +08:00
protected override PerformanceAttributes CreatePerformanceAttributes ( ScoreInfo score , DifficultyAttributes attributes )
2018-05-17 16:40:46 +08:00
{
2022-03-14 13:25:26 +08:00
var taikoAttributes = ( TaikoDifficultyAttributes ) attributes ;
countGreat = score . Statistics . GetValueOrDefault ( HitResult . Great ) ;
countOk = score . Statistics . GetValueOrDefault ( HitResult . Ok ) ;
countMeh = score . Statistics . GetValueOrDefault ( HitResult . Meh ) ;
countMiss = score . Statistics . GetValueOrDefault ( HitResult . Miss ) ;
2022-10-05 19:21:15 +08:00
accuracy = customAccuracy ;
2018-05-17 16:40:46 +08:00
2022-08-19 21:15:38 +08:00
// The effectiveMissCount is calculated by gaining a ratio for totalSuccessfulHits and increasing the miss penalty for shorter object counts lower than 1000.
2022-08-25 13:02:10 +08:00
if ( totalSuccessfulHits > 0 )
effectiveMissCount = Math . Max ( 1.0 , 1000.0 / totalSuccessfulHits ) * countMiss ;
2022-08-19 20:57:28 +08:00
double multiplier = 1.13 ;
2018-05-17 16:40:46 +08:00
2022-03-14 13:25:26 +08:00
if ( score . Mods . Any ( m = > m is ModHidden ) )
2022-07-17 12:10:49 +08:00
multiplier * = 1.075 ;
if ( score . Mods . Any ( m = > m is ModEasy ) )
multiplier * = 0.975 ;
2018-05-17 16:40:46 +08:00
2022-03-14 13:25:26 +08:00
double difficultyValue = computeDifficultyValue ( score , taikoAttributes ) ;
double accuracyValue = computeAccuracyValue ( score , taikoAttributes ) ;
2018-05-17 16:40:46 +08:00
double totalValue =
Math . Pow (
2021-12-18 04:39:03 +08:00
Math . Pow ( difficultyValue , 1.1 ) +
2018-05-17 16:40:46 +08:00
Math . Pow ( accuracyValue , 1.1 ) , 1.0 / 1.1
) * multiplier ;
2021-12-21 18:08:31 +08:00
return new TaikoPerformanceAttributes
2018-05-17 16:40:46 +08:00
{
2021-12-21 18:08:31 +08:00
Difficulty = difficultyValue ,
Accuracy = accuracyValue ,
2022-08-19 20:57:28 +08:00
EffectiveMissCount = effectiveMissCount ,
2021-12-21 18:08:31 +08:00
Total = totalValue
} ;
2018-05-17 16:40:46 +08:00
}
2022-03-14 13:25:26 +08:00
private double computeDifficultyValue ( ScoreInfo score , TaikoDifficultyAttributes attributes )
2018-05-17 16:40:46 +08:00
{
2022-07-17 12:10:49 +08:00
double difficultyValue = Math . Pow ( 5 * Math . Max ( 1.0 , attributes . StarRating / 0.115 ) - 4.0 , 2.25 ) / 1150.0 ;
2018-05-17 16:40:46 +08:00
2020-07-29 19:53:14 +08:00
double lengthBonus = 1 + 0.1 * Math . Min ( 1.0 , totalHits / 1500.0 ) ;
2021-12-18 04:39:03 +08:00
difficultyValue * = lengthBonus ;
2018-05-17 16:40:46 +08:00
2022-08-19 20:57:28 +08:00
difficultyValue * = Math . Pow ( 0.986 , effectiveMissCount ) ;
2022-07-17 12:10:49 +08:00
if ( score . Mods . Any ( m = > m is ModEasy ) )
2022-08-19 20:57:28 +08:00
difficultyValue * = 0.985 ;
2018-05-17 16:40:46 +08:00
2022-03-14 13:25:26 +08:00
if ( score . Mods . Any ( m = > m is ModHidden ) )
2021-12-18 04:39:03 +08:00
difficultyValue * = 1.025 ;
2018-05-17 16:40:46 +08:00
2022-08-19 20:57:28 +08:00
if ( score . Mods . Any ( m = > m is ModHardRock ) )
difficultyValue * = 1.050 ;
2022-03-14 13:25:26 +08:00
if ( score . Mods . Any ( m = > m is ModFlashlight < TaikoHitObject > ) )
2022-08-19 21:23:40 +08:00
difficultyValue * = 1.050 * lengthBonus ;
2018-05-17 16:40:46 +08:00
2022-10-05 19:21:15 +08:00
return difficultyValue * Math . Pow ( accuracy , 2.0 ) ;
2018-05-17 16:40:46 +08:00
}
2022-03-14 13:25:26 +08:00
private double computeAccuracyValue ( ScoreInfo score , TaikoDifficultyAttributes attributes )
2018-05-17 16:40:46 +08:00
{
2022-03-14 13:25:26 +08:00
if ( attributes . GreatHitWindow < = 0 )
2018-05-17 16:40:46 +08:00
return 0 ;
2022-10-05 19:21:15 +08:00
double accuracyValue = Math . Pow ( 60.0 / attributes . GreatHitWindow , 1.1 ) * Math . Pow ( accuracy , 8.0 ) * Math . Pow ( attributes . StarRating , 0.4 ) * 27.0 ;
2022-07-17 12:10:49 +08:00
double lengthBonus = Math . Min ( 1.15 , Math . Pow ( totalHits / 1500.0 , 0.3 ) ) ;
accuracyValue * = lengthBonus ;
2022-08-19 20:57:28 +08:00
// Slight HDFL Bonus for accuracy. A clamp is used to prevent against negative values
2022-07-17 12:10:49 +08:00
if ( score . Mods . Any ( m = > m is ModFlashlight < TaikoHitObject > ) & & score . Mods . Any ( m = > m is ModHidden ) )
2022-08-19 20:57:28 +08:00
accuracyValue * = Math . Max ( 1.050 , 1.075 * lengthBonus ) ;
2018-05-17 16:40:46 +08:00
2022-07-17 12:10:49 +08:00
return accuracyValue ;
2018-05-17 16:40:46 +08:00
}
2020-09-29 16:16:55 +08:00
private int totalHits = > countGreat + countOk + countMeh + countMiss ;
2022-08-19 20:57:28 +08:00
private int totalSuccessfulHits = > countGreat + countOk + countMeh ;
2022-10-05 19:21:15 +08:00
private double customAccuracy = > totalHits > 0 ? ( countGreat * 300 + countOk * 150 ) / ( totalHits * 300.0 ) : 0 ;
2018-05-17 16:40:46 +08:00
}
}