private void CalculateSpecificStrain(tpHitObject PreviousHitObject, AiModtpDifficulty.DifficultyType Type)
{
double Addition = 0;
double TimeElapsed = BaseHitObject.StartTime - PreviousHitObject.BaseHitObject.StartTime;
double Decay = Math.Pow(DECAY_BASE[(int)Type], TimeElapsed / 1000);
if ((BaseHitObject.Type & HitObjectType.Spinner) > 0)
{
// Do nothing for spinners
}
else if ((BaseHitObject.Type & HitObjectType.Slider) > 0)
{
switch(Type)
{
case AiModtpDifficulty.DifficultyType.Speed:
// For speed strain we treat the whole slider as a single spacing entity, since "Speed" is about how hard it is to click buttons fast.
// The spacing weight exists to differentiate between being able to easily alternate or having to single.
Addition =
SpacingWeight(PreviousHitObject.LazySliderLengthFirst +
PreviousHitObject.LazySliderLengthSubsequent * (PreviousHitObject.BaseHitObject.SegmentCount - 1) +
DistanceTo(PreviousHitObject), Type) *
SPACING_WEIGHT_SCALING[(int)Type];
break;
case AiModtpDifficulty.DifficultyType.Aim:
// For Aim strain we treat each slider segment and the jump after the end of the slider as separate jumps, since movement-wise there is no difference
// to multiple jumps.
Addition =
(
SpacingWeight(PreviousHitObject.LazySliderLengthFirst, Type) +
SpacingWeight(PreviousHitObject.LazySliderLengthSubsequent, Type) * (PreviousHitObject.BaseHitObject.SegmentCount - 1) +
SpacingWeight(DistanceTo(PreviousHitObject), Type)
) *
SPACING_WEIGHT_SCALING[(int)Type];
break;
}
}
else if ((BaseHitObject.Type & HitObjectType.Normal) > 0)
{
Addition = SpacingWeight(DistanceTo(PreviousHitObject), Type) * SPACING_WEIGHT_SCALING[(int)Type];
}
// Scale addition by the time, that elapsed. Filter out HitObjects that are too close to be played anyway to avoid crazy values by division through close to zero.
// You will never find maps that require this amongst ranked maps.
Addition /= Math.Max(TimeElapsed, 50);
Strains[(int)Type] = PreviousHitObject.Strains[(int)Type] * Decay + Addition;
}