// Caution: The subjective values are strong with this one private static double spacingWeight(double distance, OsuDifficultyCalculator.DifficultyType type) { switch (type) { case OsuDifficultyCalculator.DifficultyType.Speed: if (distance > single_spacing_threshold) { return(2.5); } else if (distance > stream_spacing_threshold) { return(1.6 + 0.9 * (distance - stream_spacing_threshold) / (single_spacing_threshold - stream_spacing_threshold)); } else if (distance > almost_diameter) { return(1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter)); } else if (distance > almost_diameter / 2) { return(0.95 + 0.25 * (distance - almost_diameter / 2) / (almost_diameter / 2)); } else { return(0.95); } case OsuDifficultyCalculator.DifficultyType.Aim: return(Math.Pow(distance, 0.99)); } Debug.Assert(false, "Invalid osu difficulty hit object type."); return(0); }
private void calculateSpecificStrain(OsuHitObjectDifficulty previousHitObject, OsuDifficultyCalculator.DifficultyType type, double timeRate) { double addition = 0; double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate; double decay = Math.Pow(DECAY_BASE[(int)type], timeElapsed / 1000); if (BaseHitObject.Type == HitObjectType.Spinner) { // Do nothing for spinners } else if (BaseHitObject.Type == HitObjectType.Slider) { switch (type) { case OsuDifficultyCalculator.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.lazySliderLength + DistanceTo(previousHitObject), type) * spacing_weight_scaling[(int)type]; break; case OsuDifficultyCalculator.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.lazySliderLength, type) + spacingWeight(DistanceTo(previousHitObject), type) ) * spacing_weight_scaling[(int)type]; break; } } else if (BaseHitObject.Type == HitObjectType.Circle) { 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; }