/// <summary> /// Computes the progress along the curve relative to how much of the <see cref="HitObject"/> has been completed. /// </summary> /// <param name="obj">The curve.</param> /// <param name="progress">[0, 1] where 0 is the start time of the <see cref="HitObject"/> and 1 is the end time of the <see cref="HitObject"/>.</param> /// <returns>[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</returns> public static double ProgressAt(this IHasPathWithRepeats obj, double progress) { double p = progress * obj.SpanCount() % 1; if (obj.SpanAt(progress) % 2 == 1) { p = 1 - p; } return(p); }
/// <summary> /// Determines which span of the curve the progress point is on. /// </summary> /// <param name="obj">The curve.</param> /// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param> /// <returns>[0, SpanCount) where 0 is the first run.</returns> public static int SpanAt(this IHasPathWithRepeats obj, double progress) => (int)(progress * obj.SpanCount());
/// <summary> /// Computes the position on the curve relative to how much of the <see cref="HitObject"/> has been completed. /// </summary> /// <param name="obj">The curve.</param> /// <param name="progress">[0, 1] where 0 is the start time of the <see cref="HitObject"/> and 1 is the end time of the <see cref="HitObject"/>.</param> /// <returns>The position on the curve.</returns> public static Vector2 CurvePositionAt(this IHasPathWithRepeats obj, double progress) => obj.Path.PositionAt(obj.ProgressAt(progress));
public static IEnumerable <BosuHitObject> ConvertBuzzSlider(HitObject obj, Vector2 originalPosition, IBeatmap beatmap, IHasPathWithRepeats curve, double spanDuration) { List <BosuHitObject> converted = new List <BosuHitObject>(); var difficulty = beatmap.BeatmapInfo.BaseDifficulty; var controlPointInfo = beatmap.ControlPointInfo; TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(obj.StartTime); DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(obj.StartTime); double scoringDistance = 100 * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier; var velocity = scoringDistance / timingPoint.BeatLength; var tickDistance = scoringDistance / difficulty.SliderTickRate; double legacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0; foreach (var e in SliderEventGenerator.Generate(obj.StartTime, spanDuration, velocity, tickDistance, curve.Path.Distance, curve.RepeatCount + 1, legacyLastTickOffset, new CancellationToken())) { var sliderEventPosition = toPlayfieldSpace(originalPosition) * new Vector2(1, 0.4f); switch (e.Type) { case SliderEventType.Head: converted.AddRange(generateExplosion(e.Time, bullets_per_slider_reverse, sliderEventPosition)); break; case SliderEventType.Repeat: converted.AddRange(generateExplosion(e.Time, bullets_per_slider_reverse, sliderEventPosition, slider_angle_per_span * (e.SpanIndex + 1))); break; case SliderEventType.Tail: converted.AddRange(generateExplosion(e.Time, bullets_per_slider_reverse, sliderEventPosition, slider_angle_per_span * (curve.RepeatCount + 1))); break; } } return(converted); }
public static IEnumerable <BosuHitObject> ConvertDefaultSlider(HitObject obj, Vector2 originalPosition, IBeatmap beatmap, IHasPathWithRepeats curve, double spanDuration) { List <BosuHitObject> converted = new List <BosuHitObject>(); var difficulty = beatmap.BeatmapInfo.BaseDifficulty; var controlPointInfo = beatmap.ControlPointInfo; TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(obj.StartTime); DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(obj.StartTime); double scoringDistance = 100 * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier; var velocity = scoringDistance / timingPoint.BeatLength; var tickDistance = scoringDistance / difficulty.SliderTickRate; double legacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0; foreach (var e in SliderEventGenerator.Generate(obj.StartTime, spanDuration, velocity, tickDistance, curve.Path.Distance, curve.RepeatCount + 1, legacyLastTickOffset, new CancellationToken())) { var curvePosition = curve.CurvePositionAt(e.PathProgress / (curve.RepeatCount + 1)) + originalPosition; var sliderEventPosition = toPlayfieldSpace(curvePosition) * new Vector2(1, 0.4f); switch (e.Type) { case SliderEventType.Repeat: converted.AddRange(generateExplosion(e.Time, Math.Clamp((int)curve.Distance / 15, 3, 15), sliderEventPosition, MathExtensions.GetRandomTimedAngleOffset(e.Time))); break; case SliderEventType.Tail: converted.AddRange(generateExplosion(e.Time, Math.Clamp((int)curve.Distance * (curve.RepeatCount + 1) / 15, 5, 20), sliderEventPosition, MathExtensions.GetRandomTimedAngleOffset(e.Time))); break; } } return(converted); }
public static IEnumerable <BosuHitObject> GenerateSliderBody(double startTime, IHasPathWithRepeats curve, Vector2 originalPosition) { var bodyCherriesCount = Math.Min(curve.Distance * (curve.RepeatCount + 1) / 10, max_visuals_per_slider_span * (curve.RepeatCount + 1)); for (int i = 0; i < bodyCherriesCount; i++) { var progress = (float)i / bodyCherriesCount; var position = curve.CurvePositionAt(progress) + originalPosition; position = toPlayfieldSpace(position) * new Vector2(1, 0.4f); yield return(new InstantCherry { StartTime = startTime + curve.Duration * progress, Position = position }); } }