private static List <SliderPartCherry> generateSliderBody(HitObject obj, IHasCurve curve, bool isKiai, int index)
        {
            var objPosition = (obj as IHasPosition)?.Position ?? Vector2.Zero;
            var comboData   = obj as IHasCombo;

            List <SliderPartCherry> hitObjects = new List <SliderPartCherry>();

            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) + objPosition;

                position *= new Vector2(1, 0.5f);

                if (positionIsValid(position))
                {
                    hitObjects.Add(new SliderPartCherry
                    {
                        StartTime      = obj.StartTime + curve.Duration * progress,
                        Position       = position,
                        NewCombo       = comboData?.NewCombo ?? false,
                        ComboOffset    = comboData?.ComboOffset ?? 0,
                        IndexInBeatmap = index,
                        IsKiai         = isKiai,
                    });
                }
            }

            return(hitObjects);
        }
		/// <summary>
		/// Returns the path progress from specified time progress which interpolates between start and end time.
		/// </summary>
		public static float GetProgress(this IHasCurve context, float progress)
		{
			float p = progress * context.SpanCount() % 1;
			// If is a repeat and is reversing back, invert progress towards end to start.
			if(context.GetSpan(progress) % 2 == 1)
				return 1 - p;
			return p;
		}
Beispiel #3
0
        /// <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 IHasCurve obj, double progress)
        {
            var p = progress * obj.SpanCount() % 1;

            if (obj.SpanAt(progress) % 2 == 1)
            {
                p = 1 - p;
            }
            return(p);
        }
Beispiel #4
0
        /// <summary>
        /// this method does not in use
        /// </summary>
        /// <param name="original"></param>
        /// <param name="beatmap"></param>
        /// <returns></returns>
        protected override IEnumerable <BaseRpObject> ConvertHitObject(HitObject original, Beatmap beatmap)
        {
            IHasCurve    curveData    = original as IHasCurve;
            IHasEndTime  endTimeData  = original as IHasEndTime;
            IHasPosition positionData = original as IHasPosition;
            IHasCombo    comboData    = original as IHasCombo;

            /*
             * if (curveData != null)
             * {
             *  yield return new RpSlider
             *  {
             *      StartTime = original.StartTime,
             *      Samples = original.Samples,
             *      CurveObject = curveData,
             *      Position = positionData?.Position ?? Vector2.Zero,
             *      NewCombo = comboData?.NewCombo ?? false
             *  };
             * }
             * else if (endTimeData != null)
             * {
             *  yield return new Spinner
             *  {
             *      StartTime = original.StartTime,
             *      Samples = original.Samples,
             *      EndTime = endTimeData.EndTime,
             *
             *      Position = positionData?.Position ?? OsuPlayfield.BASE_SIZE / 2,
             *  };
             * }
             * else
             * {
             *  yield return new HitCircle
             *  {
             *      StartTime = original.StartTime,
             *      Samples = original.Samples,
             *      Position = positionData?.Position ?? Vector2.Zero,
             *      NewCombo = comboData?.NewCombo ?? false
             *  };
             * }
             */

            yield return((BaseRpObject)original);
        }
Beispiel #5
0
        private OsuHitObject convertHitObject(HitObject original)
        {
            IHasCurve    curveData    = original as IHasCurve;
            IHasEndTime  endTimeData  = original as IHasEndTime;
            IHasPosition positionData = original as IHasPosition;
            IHasCombo    comboData    = original as IHasCombo;

            if (curveData != null)
            {
                return(new Slider
                {
                    StartTime = original.StartTime,
                    Sample = original.Sample,

                    CurveObject = curveData,

                    Position = positionData?.Position ?? Vector2.Zero,

                    NewCombo = comboData?.NewCombo ?? false
                });
            }

            if (endTimeData != null)
            {
                return(new Spinner
                {
                    StartTime = original.StartTime,
                    Sample = original.Sample,
                    Position = new Vector2(512, 384) / 2,

                    EndTime = endTimeData.EndTime
                });
            }

            return(new HitCircle
            {
                StartTime = original.StartTime,
                Sample = original.Sample,

                Position = positionData?.Position ?? Vector2.Zero,

                NewCombo = comboData?.NewCombo ?? false
            });
        }
Beispiel #6
0
 /// <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 IHasCurve obj, double progress)
 => (int)(progress * obj.SpanCount());
Beispiel #7
0
 /// <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 IHasCurve obj, double progress)
 => obj.Path.PositionAt(obj.ProgressAt(progress));
Beispiel #8
0
        protected override IEnumerable <HitObject> ConvertHitObjects(BaseHitObject hitObject)
        {
            IHasCurve     curve   = hitObject as IHasCurve;
            IHasPositionX posX    = hitObject as IHasPositionX;
            IHasEndTime   endTime = hitObject as IHasEndTime;
            IHasCombo     combo   = hitObject as IHasCombo;

            if (curve != null)
            {
                // Regenerate path using conversion method.
                SliderPath newPath = curve.Path;
                if (pixelDefinition.FromMode != GameModeType.BeatsStandard)
                {
                    Vector2[] origPoints = newPath.Points;
                    Vector2[] points     = new Vector2[origPoints.Length];
                    for (int i = 0; i < points.Length; i++)
                    {
                        points[i]   = origPoints[i];
                        points[i].x = points[i].x * pixelDefinition.Scale;
                    }
                    // Final path
                    newPath = new SliderPath(newPath.PathType, points, newPath.ExpectedDistance * pixelDefinition.Scale);
                }

                yield return(new Dragger()
                {
                    StartTime = hitObject.StartTime,
                    Samples = hitObject.Samples,
                    X = pixelDefinition.GetX(posX.X),
                    RepeatCount = curve.RepeatCount,
                    IsNewCombo = (combo == null ? false : combo.IsNewCombo),
                    ComboOffset = (combo == null ? 0 : combo.ComboOffset),
                    Path = newPath,
                    NodeSamples = curve.NodeSamples,
                    EndTime = curve.EndTime
                });
            }
            else if (endTime != null)
            {
                yield return(new Dragger()
                {
                    StartTime = hitObject.StartTime,
                    Samples = new List <SoundInfo>(),
                    X = pixelDefinition.GetX(posX.X),
                    RepeatCount = 0,
                    IsNewCombo = (combo == null ? false : combo.IsNewCombo),
                    ComboOffset = (combo == null ? 0 : combo.ComboOffset),
                    Path = new SliderPath(PathType.Linear, new Vector2[] {
                        new Vector2(0, 0),
                        new Vector2(0, 1)
                    }, null),
                    NodeSamples = new List <List <SoundInfo> >()
                    {
                        new List <SoundInfo>(),
                        hitObject.Samples
                    },
                    EndTime = endTime.EndTime
                });
            }
            else
            {
                yield return(new HitCircle()
                {
                    StartTime = hitObject.StartTime,
                    Samples = hitObject.Samples,
                    X = pixelDefinition.GetX(posX.X),
                    IsNewCombo = (combo == null ? false : combo.IsNewCombo),
                    ComboOffset = (combo == null ? 0 : combo.ComboOffset)
                });
            }
        }
Beispiel #9
0
        private void addCurveData(TextWriter writer, IHasCurve curveData, IHasPosition positionData)
        {
            PathType?lastType = null;

            for (int i = 0; i < curveData.Path.ControlPoints.Count; i++)
            {
                PathControlPoint point = curveData.Path.ControlPoints[i];

                if (point.Type.Value != null)
                {
                    if (point.Type.Value != lastType)
                    {
                        switch (point.Type.Value)
                        {
                        case PathType.Bezier:
                            writer.Write("B|");
                            break;

                        case PathType.Catmull:
                            writer.Write("C|");
                            break;

                        case PathType.PerfectCurve:
                            writer.Write("P|");
                            break;

                        case PathType.Linear:
                            writer.Write("L|");
                            break;
                        }

                        lastType = point.Type.Value;
                    }
                    else
                    {
                        // New segment with the same type - duplicate the control point
                        writer.Write(FormattableString.Invariant($"{positionData.X + point.Position.Value.X}:{positionData.Y + point.Position.Value.Y}|"));
                    }
                }

                if (i != 0)
                {
                    writer.Write(FormattableString.Invariant($"{positionData.X + point.Position.Value.X}:{positionData.Y + point.Position.Value.Y}"));
                    writer.Write(i != curveData.Path.ControlPoints.Count - 1 ? "|" : ",");
                }
            }

            writer.Write(FormattableString.Invariant($"{curveData.RepeatCount + 1},"));
            writer.Write(FormattableString.Invariant($"{curveData.Path.Distance},"));

            for (int i = 0; i < curveData.NodeSamples.Count; i++)
            {
                writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(curveData.NodeSamples[i])}"));
                writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ",");
            }

            for (int i = 0; i < curveData.NodeSamples.Count; i++)
            {
                writer.Write(getSampleBank(curveData.NodeSamples[i], true));
                writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ",");
            }
        }
		/// <summary>
		/// Returns the path span index at specified path progress.
		/// </summary>
		public static int GetSpan(this IHasCurve context, float progress)
		{
			return (int)(progress * context.SpanCount());
		}
		/// <summary>
		/// Returns the position on the path at specified time progress.
		/// </summary>
		public static Vector2 GetPosition(this IHasCurve context, float progress)
		{
			return context.Path.GetPosition(context.GetProgress(progress));
		}
        public static List <TouhouHitObject> ConvertSlider(HitObject obj, IBeatmap beatmap, IHasCurve curve, bool isKiai, int index)
        {
            double spanDuration = curve.Duration / (curve.RepeatCount + 1);
            bool   isRepeatSpam = spanDuration < 75 && curve.RepeatCount > 0;

            if (isRepeatSpam)
            {
                return(generateRepeatSpamSlider(obj, beatmap, curve, spanDuration, isKiai, index));
            }
            else
            {
                return(generateDefaultSlider(obj, beatmap, curve, spanDuration, isKiai, index));
            }
        }
        private static List <TouhouHitObject> generateRepeatSpamSlider(HitObject obj, IBeatmap beatmap, IHasCurve curve, double spanDuration, bool isKiai, int index)
        {
            List <TouhouHitObject> hitObjects = new List <TouhouHitObject>();

            var objPosition = (obj as IHasPosition)?.Position ?? Vector2.Zero;
            var comboData   = obj as IHasCombo;
            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))
            {
                var sliderEventPosition = objPosition * new Vector2(1, 0.5f);

                switch (e.Type)
                {
                case SliderEventType.Head:

                    if (positionIsValid(sliderEventPosition))
                    {
                        hitObjects.AddRange(generateExplosion(
                                                e.Time,
                                                bullets_per_slider_reverse,
                                                sliderEventPosition,
                                                comboData,
                                                isKiai,
                                                index));
                    }

                    hitObjects.Add(new SoundHitObject
                    {
                        StartTime = obj.StartTime,
                        Samples   = obj.Samples
                    });

                    break;

                case SliderEventType.Repeat:

                    if (positionIsValid(sliderEventPosition))
                    {
                        hitObjects.AddRange(generateExplosion(
                                                e.Time,
                                                bullets_per_slider_reverse,
                                                sliderEventPosition,
                                                comboData,
                                                isKiai,
                                                index,
                                                slider_angle_per_span * (e.SpanIndex + 1)));
                    }

                    hitObjects.Add(new SoundHitObject
                    {
                        StartTime = e.Time,
                        Samples   = obj.Samples
                    });
                    break;

                case SliderEventType.Tail:

                    if (positionIsValid(sliderEventPosition))
                    {
                        hitObjects.AddRange(generateExplosion(
                                                e.Time,
                                                bullets_per_slider_reverse,
                                                sliderEventPosition,
                                                comboData,
                                                isKiai,
                                                index,
                                                slider_angle_per_span * (curve.RepeatCount + 1)));
                    }

                    hitObjects.Add(new SoundHitObject
                    {
                        StartTime = curve.EndTime,
                        Samples   = obj.Samples
                    });
                    break;
                }
            }

            hitObjects.AddRange(generateSliderBody(obj, curve, isKiai, index));

            return(hitObjects);
        }
        private static List <TouhouHitObject> generateDefaultSlider(HitObject obj, IBeatmap beatmap, IHasCurve curve, double spanDuration, bool isKiai, int index)
        {
            List <TouhouHitObject> hitObjects = new List <TouhouHitObject>();

            var objPosition = (obj as IHasPosition)?.Position ?? Vector2.Zero;
            var comboData   = obj as IHasCombo;
            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))
            {
                var sliderEventPosition = (curve.CurvePositionAt(e.PathProgress / (curve.RepeatCount + 1)) + objPosition) * new Vector2(1, 0.5f);

                switch (e.Type)
                {
                case SliderEventType.Head:

                    hitObjects.Add(new SoundHitObject
                    {
                        StartTime = obj.StartTime,
                        Samples   = obj.Samples
                    });

                    break;

                case SliderEventType.Tick:

                    if (positionIsValid(sliderEventPosition))
                    {
                        hitObjects.Add(new TickCherry
                        {
                            Angle          = 180,
                            StartTime      = e.Time,
                            Position       = sliderEventPosition,
                            NewCombo       = comboData?.NewCombo ?? false,
                            ComboOffset    = comboData?.ComboOffset ?? 0,
                            IndexInBeatmap = index,
                            IsKiai         = isKiai
                        });
                    }

                    hitObjects.Add(new SoundHitObject
                    {
                        StartTime = e.Time,
                        Samples   = getTickSamples(obj.Samples)
                    });
                    break;

                case SliderEventType.Repeat:

                    if (positionIsValid(sliderEventPosition))
                    {
                        hitObjects.AddRange(generateTriangularExplosion(
                                                e.Time,
                                                20,
                                                sliderEventPosition,
                                                comboData,
                                                isKiai,
                                                index,
                                                MathExtensions.GetRandomTimedAngleOffset(e.Time)));
                    }

                    hitObjects.Add(new SoundHitObject
                    {
                        StartTime = e.Time,
                        Samples   = obj.Samples
                    });
                    break;

                case SliderEventType.Tail:

                    if (positionIsValid(sliderEventPosition))
                    {
                        hitObjects.AddRange(generateExplosion(
                                                e.Time,
                                                Math.Clamp((int)curve.Distance / 15, 5, 20),
                                                sliderEventPosition,
                                                comboData,
                                                isKiai,
                                                index));
                    }

                    hitObjects.Add(new SoundHitObject
                    {
                        StartTime = curve.EndTime,
                        Samples   = obj.Samples
                    });
                    break;
                }
            }

            hitObjects.AddRange(generateSliderBody(obj, curve, isKiai, index));

            return(hitObjects);
        }