// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { public class Strain : Skill { private const double rhythm_change_base_threshold = 0.2; private const double rhythm_change_base = 2.0; protected override double SkillMultiplier => 1; protected override double StrainDecayBase => 0.3; private ColourSwitch lastColourSwitch = ColourSwitch.None; private int sameColourCount = 1; protected override double StrainValueOf(DifficultyHitObject current) { double addition = 1; // We get an extra addition if we are not a slider or spinner if (current.LastObject is Hit && current.BaseObject is Hit && current.BaseObject.StartTime - current.LastObject.StartTime < 1000) { if (hasColourChange(current)) addition += 0.75; if (hasRhythmChange(current)) addition += 1; } else { lastColourSwitch = ColourSwitch.None; sameColourCount = 1; } double additionFactor = 1; // Scale the addition factor linearly from 0.4 to 1 for DeltaTime from 0 to 50 if (current.DeltaTime < 50) additionFactor = 0.4 + 0.6 * current.DeltaTime / 50; return additionFactor * addition; } private bool hasRhythmChange(DifficultyHitObject current) { // We don't want a division by zero if some random mapper decides to put two HitObjects at the same time. if (current.DeltaTime == 0 || Previous.Count == 0 || Previous[0].DeltaTime == 0) return false; double timeElapsedRatio = Math.Max(Previous[0].DeltaTime / current.DeltaTime, current.DeltaTime / Previous[0].DeltaTime); if (timeElapsedRatio >= 8) return false; double difference = Math.Log(timeElapsedRatio, rhythm_change_base) % 1.0; return difference > rhythm_change_base_threshold && difference < 1 - rhythm_change_base_threshold; } private bool hasColourChange(DifficultyHitObject current) { var taikoCurrent = (TaikoDifficultyHitObject)current; if (!taikoCurrent.HasTypeChange) { sameColourCount++; return false; } var oldColourSwitch = lastColourSwitch; var newColourSwitch = sameColourCount % 2 == 0 ? ColourSwitch.Even : ColourSwitch.Odd; lastColourSwitch = newColourSwitch; sameColourCount = 1; // We only want a bonus if the parity of the color switch changes return oldColourSwitch != ColourSwitch.None && oldColourSwitch != newColourSwitch; } private enum ColourSwitch { None, Even, Odd } } }