2019-02-18 13:46:32 +08:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2019-01-24 16:43:03 +08:00
// See the LICENCE file in the repository root for full licence text.
2018-04-13 17:19:50 +08:00
2018-05-21 09:49:23 +08:00
using System ;
2018-06-21 11:26:15 +08:00
using System.Collections.Generic ;
2018-06-21 12:12:22 +08:00
using System.Linq ;
2018-05-15 16:40:19 +08:00
using osu.Game.Beatmaps ;
2019-02-18 13:46:32 +08:00
using osu.Game.Rulesets.Catch.Difficulty.Preprocessing ;
using osu.Game.Rulesets.Catch.Difficulty.Skills ;
2019-03-14 22:06:23 +08:00
using osu.Game.Rulesets.Catch.Mods ;
2018-05-21 09:49:23 +08:00
using osu.Game.Rulesets.Catch.Objects ;
using osu.Game.Rulesets.Catch.UI ;
2019-02-18 13:46:32 +08:00
using osu.Game.Rulesets.Difficulty ;
using osu.Game.Rulesets.Difficulty.Preprocessing ;
using osu.Game.Rulesets.Difficulty.Skills ;
2019-02-19 16:42:24 +08:00
using osu.Game.Rulesets.Mods ;
2018-04-13 17:19:50 +08:00
2018-05-15 16:40:19 +08:00
namespace osu.Game.Rulesets.Catch.Difficulty
2018-04-13 17:19:50 +08:00
{
2018-04-19 21:04:12 +08:00
public class CatchDifficultyCalculator : DifficultyCalculator
2018-04-13 17:19:50 +08:00
{
2019-02-18 13:46:32 +08:00
private const double star_scaling_factor = 0.145 ;
2018-05-21 09:49:23 +08:00
2019-02-18 13:46:32 +08:00
protected override int SectionLength = > 750 ;
2018-05-21 09:49:23 +08:00
2018-06-21 11:26:15 +08:00
public CatchDifficultyCalculator ( Ruleset ruleset , WorkingBeatmap beatmap )
: base ( ruleset , beatmap )
2018-04-13 17:19:50 +08:00
{
2019-03-19 14:35:14 +08:00
}
2019-02-19 16:42:24 +08:00
protected override DifficultyAttributes CreateDifficultyAttributes ( IBeatmap beatmap , Mod [ ] mods , Skill [ ] skills , double clockRate )
2019-02-18 13:46:32 +08:00
{
2019-02-19 16:42:24 +08:00
if ( beatmap . HitObjects . Count = = 0 )
2019-02-19 16:45:52 +08:00
return new CatchDifficultyAttributes { Mods = mods } ;
2018-05-21 09:49:23 +08:00
2018-06-21 16:32:10 +08:00
// this is the same as osu!, so there's potential to share the implementation... maybe
2019-02-19 16:42:24 +08:00
double preempt = BeatmapDifficulty . DifficultyRange ( beatmap . BeatmapInfo . BaseDifficulty . ApproachRate , 1800 , 1200 , 450 ) / clockRate ;
2018-05-21 09:49:23 +08:00
2019-02-19 16:42:24 +08:00
return new CatchDifficultyAttributes
2018-06-21 11:26:15 +08:00
{
2019-02-19 16:42:24 +08:00
StarRating = Math . Sqrt ( skills [ 0 ] . DifficultyValue ( ) ) * star_scaling_factor ,
2019-02-19 16:45:52 +08:00
Mods = mods ,
2018-07-05 10:32:09 +08:00
ApproachRate = preempt > 1200.0 ? - ( preempt - 1800.0 ) / 120.0 : - ( preempt - 1200.0 ) / 150.0 + 5.0 ,
2019-02-19 16:42:24 +08:00
MaxCombo = beatmap . HitObjects . Count ( h = > h is Fruit ) + beatmap . HitObjects . OfType < JuiceStream > ( ) . SelectMany ( j = > j . NestedHitObjects ) . Count ( h = > ! ( h is TinyDroplet ) )
2018-06-21 11:26:15 +08:00
} ;
2018-05-21 09:49:23 +08:00
}
2019-02-19 16:42:24 +08:00
protected override IEnumerable < DifficultyHitObject > CreateDifficultyHitObjects ( IBeatmap beatmap , double clockRate )
2018-05-21 09:49:23 +08:00
{
2019-03-19 14:59:04 +08:00
float halfCatchWidth ;
using ( var catcher = new CatcherArea . Catcher ( beatmap . BeatmapInfo . BaseDifficulty ) )
2019-03-19 14:53:27 +08:00
{
halfCatchWidth = catcher . CatchWidth * 0.5f ;
2019-03-19 14:59:04 +08:00
halfCatchWidth * = 0.8f ; // We're only using 80% of the catcher's width to simulate imperfect gameplay.
2019-03-19 14:53:27 +08:00
}
2019-02-18 13:46:32 +08:00
CatchHitObject lastObject = null ;
2018-05-21 09:49:23 +08:00
2019-02-18 13:46:32 +08:00
foreach ( var hitObject in beatmap . HitObjects . OfType < CatchHitObject > ( ) )
2018-06-21 15:21:08 +08:00
{
2019-02-18 13:46:32 +08:00
if ( lastObject = = null )
2018-05-21 09:49:23 +08:00
{
2019-02-18 13:46:32 +08:00
lastObject = hitObject ;
continue ;
2018-05-21 09:49:23 +08:00
}
2019-02-18 13:46:32 +08:00
switch ( hitObject )
{
// We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations.
case Fruit fruit :
2019-03-19 14:59:04 +08:00
yield return new CatchDifficultyHitObject ( fruit , lastObject , clockRate , halfCatchWidth ) ;
2019-02-28 12:31:40 +08:00
2019-02-18 13:48:19 +08:00
lastObject = hitObject ;
2019-02-18 13:46:32 +08:00
break ;
case JuiceStream _ :
foreach ( var nested in hitObject . NestedHitObjects . OfType < CatchHitObject > ( ) . Where ( o = > ! ( o is TinyDroplet ) ) )
2019-02-18 13:48:19 +08:00
{
2019-03-19 14:59:04 +08:00
yield return new CatchDifficultyHitObject ( nested , lastObject , clockRate , halfCatchWidth ) ;
2019-02-28 12:31:40 +08:00
2019-02-18 13:48:19 +08:00
lastObject = nested ;
}
2019-02-28 12:31:40 +08:00
2019-02-18 13:46:32 +08:00
break ;
}
2018-06-21 11:26:15 +08:00
}
2018-05-21 09:49:23 +08:00
}
2019-02-18 13:46:32 +08:00
2019-02-19 16:54:00 +08:00
protected override Skill [ ] CreateSkills ( IBeatmap beatmap ) = > new Skill [ ]
2019-02-18 13:46:32 +08:00
{
new Movement ( ) ,
} ;
2019-03-14 22:06:23 +08:00
protected override Mod [ ] DifficultyAdjustmentMods = > new Mod [ ]
{
new CatchModDoubleTime ( ) ,
new CatchModHalfTime ( ) ,
new CatchModHardRock ( ) ,
new CatchModEasy ( ) ,
} ;
2018-04-13 17:19:50 +08:00
}
}