public DifficultyPointPiece(DifficultyControlPoint point)
            : base(point)
        {
            speedMultiplier = point.SpeedMultiplierBindable.GetBoundCopy();

            Y = Height;
        }
Beispiel #2
0
        public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap)
            : base(random, hitObject, beatmap, previousPattern, originalBeatmap)
        {
            convertType = PatternType.None;
            if (!Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
            {
                convertType = PatternType.LowProbability;
            }

            var distanceData = hitObject as IHasDistance;
            var repeatsData  = hitObject as IHasRepeats;

            spanCount = repeatsData?.SpanCount() ?? 1;

            TimingControlPoint     timingPoint     = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
            DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(hitObject.StartTime);

            // The true distance, accounting for any repeats
            double distance = (distanceData?.Distance ?? 0) * spanCount;
            // The velocity of the osu! hit object - calculated as the velocity of a slider
            double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength;
            // The duration of the osu! hit object
            double osuDuration = distance / osuVelocity;

            EndTime         = hitObject.StartTime + osuDuration;
            SegmentDuration = (EndTime - HitObject.StartTime) / spanCount;
        }
Beispiel #3
0
        private void load()
        {
            // Calculate default multiplier control points
            var lastTimingPoint     = new TimingControlPoint();
            var lastDifficultyPoint = new DifficultyControlPoint();

            // Merge timing + difficulty points
            var allPoints = new SortedList <ControlPoint>(Comparer <ControlPoint> .Default);

            allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints);
            allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints);

            // Generate the timing points, making non-timing changes use the previous timing change
            var timingChanges = allPoints.Select(c =>
            {
                var timingPoint     = c as TimingControlPoint;
                var difficultyPoint = c as DifficultyControlPoint;

                if (timingPoint != null)
                {
                    lastTimingPoint = timingPoint;
                }

                if (difficultyPoint != null)
                {
                    lastDifficultyPoint = difficultyPoint;
                }

                return(new MultiplierControlPoint(c.Time)
                {
                    Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier,
                    TimingPoint = lastTimingPoint,
                    DifficultyPoint = lastDifficultyPoint
                });
            });

            double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;

            // Perform some post processing of the timing changes
            timingChanges = timingChanges
                            // Collapse sections after the last hit object
                            .Where(s => s.StartTime <= lastObjectTime)
                            // Collapse sections with the same start time
                            .GroupBy(s => s.StartTime).Select(g => g.Last()).OrderBy(s => s.StartTime);

            DefaultControlPoints.AddRange(timingChanges);

            // If we have no control points, add a default one
            if (DefaultControlPoints.Count == 0)
            {
                DefaultControlPoints.Add(new MultiplierControlPoint {
                    Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier
                });
            }

            DefaultControlPoints.ForEach(c => applySpeedAdjustment(c, Playfield));
        }
        protected override float GetVelocity(double time, ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
        {
            TimingControlPoint     timingPoint     = controlPointInfo.TimingPointAt(time);
            DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(time);

            double scoringDistance = OsuHitObject.BASE_SCORING_DISTANCE * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;

            return((float)(scoringDistance / timingPoint.BeatLength));
        }
        protected override void ApplyMapPropertiesSelf(ControlPointGroup controlPoints, MapDifficulty difficulty)
        {
            base.ApplyMapPropertiesSelf(controlPoints, difficulty);

            TimingControlPoint     timingPoint     = controlPoints.TimingPointAt(StartTime);
            DifficultyControlPoint difficultyPoint = controlPoints.DifficultyPointAt(StartTime);

            float distance = BaseDistance * difficultyPoint.SpeedMultiplier * difficulty.SliderMultiplier;

            Speed = distance / timingPoint.BeatLength;
        }
Beispiel #6
0
        protected override void ApplyBeatmap()
        {
            // Calculate default multiplier control points
            var lastTimingPoint     = new TimingControlPoint();
            var lastDifficultyPoint = new DifficultyControlPoint();

            // Merge timing + difficulty points
            var allPoints = new SortedList <ControlPoint>(Comparer <ControlPoint> .Default);

            allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints);
            allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints);

            // Generate the timing points, making non-timing changes use the previous timing change
            var timingChanges = allPoints.Select(c =>
            {
                var timingPoint     = c as TimingControlPoint;
                var difficultyPoint = c as DifficultyControlPoint;

                if (timingPoint != null)
                {
                    lastTimingPoint = timingPoint;
                }

                if (difficultyPoint != null)
                {
                    lastDifficultyPoint = difficultyPoint;
                }

                return(new MultiplierControlPoint(c.Time)
                {
                    TimingPoint = lastTimingPoint,
                    DifficultyPoint = lastDifficultyPoint
                });
            });

            double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;

            // Perform some post processing of the timing changes
            timingChanges = timingChanges
                            // Collapse sections after the last hit object
                            .Where(s => s.StartTime <= lastObjectTime)
                            // Collapse sections with the same start time
                            .GroupBy(s => s.StartTime).Select(g => g.Last()).OrderBy(s => s.StartTime)
                            // Collapse sections with the same beat length
                            .GroupBy(s => s.TimingPoint.BeatLength * s.DifficultyPoint.SpeedMultiplier).Select(g => g.First());

            DefaultControlPoints.AddRange(timingChanges);

            // If we have no control points, add a default one
            if (DefaultControlPoints.Count == 0)
            {
                DefaultControlPoints.Add(new MultiplierControlPoint());
            }
        }
Beispiel #7
0
        protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
        {
            base.ApplyDefaultsToSelf(controlPointInfo, difficulty);

            TimingControlPoint     timingPoint     = controlPointInfo.TimingPointAt(StartTime);
            DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);

            double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;

            Velocity = scoringDistance / timingPoint.BeatLength;
        }
Beispiel #8
0
        private void handleDifficultyControlPoint(DifficultyControlPoint newPoint)
        {
            var existing = beatmap.ControlPointInfo.DifficultyPointAt(newPoint.Time);

            if (newPoint.EquivalentTo(existing))
            {
                return;
            }

            beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == newPoint.Time);
            beatmap.ControlPointInfo.DifficultyPoints.Add(newPoint);
        }
Beispiel #9
0
        public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
        {
            base.ApplyDefaults(controlPointInfo, difficulty);

            TimingControlPoint     timingPoint     = controlPointInfo.TimingPointAt(StartTime);
            DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
            EffectControlPoint     effectPoint     = controlPointInfo.EffectPointAt(StartTime);

            ScrollTime = scroll_time * (timingPoint.BeatLength * difficultyPoint.SpeedMultiplier / 1000) / difficulty.SliderMultiplier;

            Kiai |= effectPoint.KiaiMode;
        }
Beispiel #10
0
        public static List <SentakkiHitObject> CreateTapFromTicks(HitObject original, int path, IBeatmap beatmap, Random rng)
        {
            var    curve        = original as IHasCurve;
            double spanDuration = curve.Duration / (curve.RepeatCount + 1);
            bool   isRepeatSpam = spanDuration < 75 && curve.RepeatCount > 0;

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

            if (isRepeatSpam)
            {
                return(hitObjects);
            }

            var difficulty = beatmap.BeatmapInfo.BaseDifficulty;

            var controlPointInfo = beatmap.ControlPointInfo;
            TimingControlPoint     timingPoint     = controlPointInfo.TimingPointAt(original.StartTime);
            DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(original.StartTime);

            double scoringDistance = 100 * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;

            var velocity     = scoringDistance / timingPoint.BeatLength;
            var tickDistance = scoringDistance / difficulty.SliderTickRate;

            double legacyLastTickOffset = (original as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0;

            foreach (var e in SliderEventGenerator.Generate(original.StartTime, spanDuration, velocity, tickDistance, curve.Path.Distance, curve.RepeatCount + 1, legacyLastTickOffset))
            {
                int newPath = path;
                while (newPath == path)
                {
                    newPath = rng.Next(0, 8);
                }

                switch (e.Type)
                {
                case SliderEventType.Tick:
                case SliderEventType.Repeat:
                    hitObjects.Add(new Tap
                    {
                        NoteColor   = Color4.Orange,
                        Angle       = newPath.GetAngleFromPath(),
                        Samples     = getTickSamples(original.Samples),
                        StartTime   = e.Time,
                        EndPosition = SentakkiExtensions.GetPosition(SentakkiPlayfield.INTERSECTDISTANCE, newPath),
                        Position    = SentakkiExtensions.GetPosition(SentakkiPlayfield.NOTESTARTDISTANCE, newPath),
                    });
                    break;
                }
            }
            return(hitObjects);
        }
            public TestSlider()
            {
                DifficultyControlPoint = new DifficultyControlPoint {
                    SliderVelocity = 0.1f
                };

                DefaultsApplied += _ =>
                {
                    HeadCircle.HitWindows = new TestHitWindows();
                    TailCircle.HitWindows = new TestHitWindows();

                    HeadCircle.HitWindows.SetDifficulty(0);
                    TailCircle.HitWindows.SetDifficulty(0);
                };
            }
        /// <summary>
        /// Adds specified difficulty point to the beatmap.
        /// </summary>
        private void AddDifficultyPoint(DifficultyControlPoint point)
        {
            // Replace existing point if equal time.
            var existingPoint = map.ControlPoints.DifficultyPointAt(point.Time);

            if (point.IsEquivalentTo(existingPoint))
            {
                return;
            }
            if (existingPoint.Time == point.Time)
            {
                map.ControlPoints.DifficultyPoints.Remove(existingPoint);
            }

            map.ControlPoints.DifficultyPoints.Add(point);
        }
Beispiel #13
0
        public void TestRemoveControlPointFromGroup()
        {
            var cpi = new ControlPointInfo();

            var group = cpi.GroupAt(1000, true);

            Assert.That(cpi.Groups.Count, Is.EqualTo(1));

            var difficultyPoint = new DifficultyControlPoint();

            group.Add(difficultyPoint);
            group.Remove(difficultyPoint);

            Assert.That(group.ControlPoints.Count, Is.EqualTo(0));
            Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(0));
            Assert.That(cpi.AllControlPoints.Count, Is.EqualTo(0));
        }
Beispiel #14
0
        private void handleDifficultyControlPoint(DifficultyControlPoint newPoint)
        {
            var existing = beatmap.ControlPointInfo.DifficultyPointAt(newPoint.Time);

            if (existing.Time == newPoint.Time)
            {
                // autogenerated points should not replace non-autogenerated.
                // this allows for incorrectly ordered timing points to still be correctly handled.
                if (newPoint.AutoGenerated && !existing.AutoGenerated)
                {
                    return;
                }

                beatmap.ControlPointInfo.DifficultyPoints.Remove(existing);
            }

            beatmap.ControlPointInfo.DifficultyPoints.Add(newPoint);
        }
Beispiel #15
0
        private IEnumerable <SentakkiHitObject> createTapsFromTicks(HitObject original)
        {
            int noteLane = getNewLane(true);

            var    curve        = original as IHasPathWithRepeats;
            double spanDuration = curve.Duration / (curve.RepeatCount + 1);
            bool   isRepeatSpam = spanDuration < 75 && curve.RepeatCount > 0;

            if (isRepeatSpam)
            {
                yield break;
            }

            var difficulty = beatmap.BeatmapInfo.BaseDifficulty;

            var controlPointInfo = beatmap.ControlPointInfo;
            TimingControlPoint     timingPoint     = controlPointInfo.TimingPointAt(original.StartTime);
            DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(original.StartTime);

            double scoringDistance = 100 * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;

            var velocity     = scoringDistance / timingPoint.BeatLength;
            var tickDistance = scoringDistance / difficulty.SliderTickRate;

            double legacyLastTickOffset = (original as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0;

            foreach (var e in SliderEventGenerator.Generate(original.StartTime, spanDuration, velocity, tickDistance, curve.Path.Distance, curve.RepeatCount + 1, legacyLastTickOffset, CancellationToken.None))
            {
                switch (e.Type)
                {
                case SliderEventType.Tick:
                case SliderEventType.Repeat:
                    yield return(new Tap
                    {
                        Lane = noteLane,
                        Samples = original.Samples.Select(s => new HitSampleInfo(@"slidertick", s.Bank, s.Suffix, s.Volume)).ToList(),
                        StartTime = e.Time
                    });

                    break;
                }
            }
        }
Beispiel #16
0
            private void load()
            {
                Children = new Drawable[]
                {
                    new FillFlowContainer
                    {
                        Width        = 200,
                        Direction    = FillDirection.Vertical,
                        AutoSizeAxes = Axes.Y,
                        Children     = new Drawable[]
                        {
                            sliderVelocitySlider = new SliderWithTextBoxInput <double>("Velocity")
                            {
                                Current      = new DifficultyControlPoint().SliderVelocityBindable,
                                KeyboardStep = 0.1f
                            },
                            new OsuTextFlowContainer
                            {
                                AutoSizeAxes     = Axes.Y,
                                RelativeSizeAxes = Axes.X,
                                Text             = "Hold shift while dragging the end of an object to adjust velocity while snapping."
                            }
                        }
                    }
                };

                var selectedPointBindable = point.SliderVelocityBindable;

                // there may be legacy control points, which contain infinite precision for compatibility reasons (see LegacyDifficultyControlPoint).
                // generally that level of precision could only be set by externally editing the .osu file, so at the point
                // a user is looking to update this within the editor it should be safe to obliterate this additional precision.
                double expectedPrecision = new DifficultyControlPoint().SliderVelocityBindable.Precision;

                if (selectedPointBindable.Precision < expectedPrecision)
                {
                    selectedPointBindable.Precision = expectedPrecision;
                }

                sliderVelocitySlider.Current = selectedPointBindable;
                sliderVelocitySlider.Current.BindValueChanged(_ => beatmap?.Update(hitObject));
            }
        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 DistanceObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap)
            : base(random, hitObject, beatmap, previousPattern, originalBeatmap)
        {
            convertType = PatternType.None;
            if (!Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
            {
                convertType = PatternType.LowProbability;
            }

            var distanceData = hitObject as IHasDistance;
            var repeatsData  = hitObject as IHasRepeats;

            Debug.Assert(distanceData != null);

            TimingControlPoint     timingPoint     = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
            DifficultyControlPoint difficultyPoint = hitObject.DifficultyControlPoint;

            double beatLength;

#pragma warning disable 618
            if (difficultyPoint is LegacyBeatmapDecoder.LegacyDifficultyControlPoint legacyDifficultyPoint)
#pragma warning restore 618
            {
                beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier;
            }
            else
            {
                beatLength = timingPoint.BeatLength / difficultyPoint.SliderVelocity;
            }

            SpanCount = repeatsData?.SpanCount() ?? 1;
            StartTime = (int)Math.Round(hitObject.StartTime);

            // This matches stable's calculation.
            EndTime = (int)Math.Floor(StartTime + distanceData.Distance * beatLength * SpanCount * 0.01 / beatmap.Difficulty.SliderMultiplier);

            SegmentDuration = (EndTime - StartTime) / SpanCount;
        }
        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);
        }
        protected override IEnumerable <TaikoHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap)
        {
            var distanceData = obj as IHasDistance;
            var repeatsData  = obj as IHasRepeats;
            var endTimeData  = obj as IHasEndTime;
            var curveData    = obj as IHasCurve;

            // Old osu! used hit sounding to determine various hit type information
            List <SampleInfo> samples = obj.Samples;

            bool strong = samples.Any(s => s.Name == SampleInfo.HIT_FINISH);

            if (distanceData != null)
            {
                // Number of spans of the object - one for the initial length and for each repeat
                int spans = repeatsData?.SpanCount() ?? 1;

                TimingControlPoint     timingPoint     = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime);
                DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime);

                double speedAdjustment         = difficultyPoint.SpeedMultiplier;
                double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment;

                // The true distance, accounting for any repeats. This ends up being the drum roll distance later
                double distance = distanceData.Distance * spans * legacy_velocity_multiplier;

                // The velocity of the taiko hit object - calculated as the velocity of a drum roll
                double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / speedAdjustedBeatLength;
                // The duration of the taiko hit object
                double taikoDuration = distance / taikoVelocity;

                // The velocity of the osu! hit object - calculated as the velocity of a slider
                double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / speedAdjustedBeatLength;
                // The duration of the osu! hit object
                double osuDuration = distance / osuVelocity;

                // osu-stable always uses the speed-adjusted beatlength to determine the velocities, but
                // only uses it for tick rate if beatmap version < 8
                if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
                {
                    speedAdjustedBeatLength *= speedAdjustment;
                }

                // If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
                double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans);

                if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
                {
                    List <List <SampleInfo> > allSamples = curveData != null ? curveData.NodeSamples : new List <List <SampleInfo> >(new[] { samples });

                    int i = 0;
                    for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)
                    {
                        List <SampleInfo> currentSamples = allSamples[i];
                        bool isRim = currentSamples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);
                        strong = currentSamples.Any(s => s.Name == SampleInfo.HIT_FINISH);

                        if (isRim)
                        {
                            yield return(new RimHit
                            {
                                StartTime = j,
                                Samples = currentSamples,
                                IsStrong = strong
                            });
                        }
                        else
                        {
                            yield return(new CentreHit
                            {
                                StartTime = j,
                                Samples = currentSamples,
                                IsStrong = strong
                            });
                        }

                        i = (i + 1) % allSamples.Count;
                    }
                }
                else
                {
                    yield return(new DrumRoll
                    {
                        StartTime = obj.StartTime,
                        Samples = obj.Samples,
                        IsStrong = strong,
                        Duration = taikoDuration,
                        TickRate = beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate == 3 ? 3 : 4
                    });
                }
            }
            else if (endTimeData != null)
            {
                double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;

                yield return(new Swell
                {
                    StartTime = obj.StartTime,
                    Samples = obj.Samples,
                    Duration = endTimeData.Duration,
                    RequiredHits = (int)Math.Max(1, endTimeData.Duration / 1000 * hitMultiplier)
                });
            }
            else
            {
                bool isRim = samples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);

                if (isRim)
                {
                    yield return(new RimHit
                    {
                        StartTime = obj.StartTime,
                        Samples = obj.Samples,
                        IsStrong = strong
                    });
                }
                else
                {
                    yield return(new CentreHit
                    {
                        StartTime = obj.StartTime,
                        Samples = obj.Samples,
                        IsStrong = strong
                    });
                }
            }
        }
Beispiel #21
0
        private void handleTimingPoints(Beatmap beatmap, string line)
        {
            string[] split = line.Split(',');

            double time            = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo);
            double beatLength      = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo);
            double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1;

            TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple;

            if (split.Length >= 3)
            {
                timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)int.Parse(split[2]);
            }

            LegacySampleBank sampleSet = defaultSampleBank;

            if (split.Length >= 4)
            {
                sampleSet = (LegacySampleBank)int.Parse(split[3]);
            }

            //SampleBank sampleBank = SampleBank.Default;
            //if (split.Length >= 5)
            //    sampleBank = (SampleBank)int.Parse(split[4]);

            int sampleVolume = defaultSampleVolume;

            if (split.Length >= 6)
            {
                sampleVolume = int.Parse(split[5]);
            }

            bool timingChange = true;

            if (split.Length >= 7)
            {
                timingChange = split[6][0] == '1';
            }

            bool kiaiMode = false;
            bool omitFirstBarSignature = false;

            if (split.Length >= 8)
            {
                int effectFlags = int.Parse(split[7]);
                kiaiMode = (effectFlags & 1) > 0;
                omitFirstBarSignature = (effectFlags & 8) > 0;
            }

            string stringSampleSet = sampleSet.ToString().ToLower();

            if (stringSampleSet == @"none")
            {
                stringSampleSet = @"normal";
            }

            DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time);
            SoundControlPoint      soundPoint      = beatmap.ControlPointInfo.SoundPointAt(time);
            EffectControlPoint     effectPoint     = beatmap.ControlPointInfo.EffectPointAt(time);

            if (timingChange)
            {
                beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint
                {
                    Time          = time,
                    BeatLength    = beatLength,
                    TimeSignature = timeSignature
                });
            }

            if (speedMultiplier != difficultyPoint.SpeedMultiplier)
            {
                beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time);
                beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint
                {
                    Time            = time,
                    SpeedMultiplier = speedMultiplier
                });
            }

            if (stringSampleSet != soundPoint.SampleBank || sampleVolume != soundPoint.SampleVolume)
            {
                beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint
                {
                    Time         = time,
                    SampleBank   = stringSampleSet,
                    SampleVolume = sampleVolume
                });
            }

            if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine)
            {
                beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint
                {
                    Time             = time,
                    KiaiMode         = kiaiMode,
                    OmitFirstBarLine = omitFirstBarSignature
                });
            }
        }
Beispiel #22
0
        public override float GetBeatSnapDistanceAt(double referenceTime)
        {
            DifficultyControlPoint difficultyPoint = EditorBeatmap.ControlPointInfo.DifficultyPointAt(referenceTime);

            return((float)(100 * EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / BeatSnapProvider.BeatDivisor));
        }
        private void handleControlPoints(TextWriter writer)
        {
            if (beatmap.ControlPointInfo.Groups.Count == 0)
            {
                return;
            }

            var legacyControlPoints = new LegacyControlPointInfo();

            foreach (var point in beatmap.ControlPointInfo.AllControlPoints)
            {
                legacyControlPoints.Add(point.Time, point.DeepClone());
            }

            writer.WriteLine("[TimingPoints]");

            SampleControlPoint     lastRelevantSamplePoint     = null;
            DifficultyControlPoint lastRelevantDifficultyPoint = null;

            bool isOsuRuleset = beatmap.BeatmapInfo.RulesetID == 0;

            // iterate over hitobjects and pull out all required sample and difficulty changes
            extractDifficultyControlPoints(beatmap.HitObjects);
            extractSampleControlPoints(beatmap.HitObjects);

            // handle scroll speed, which is stored as "slider velocity" in legacy formats.
            // this is relevant for scrolling ruleset beatmaps.
            if (!isOsuRuleset)
            {
                foreach (var point in legacyControlPoints.EffectPoints)
                {
                    legacyControlPoints.Add(point.Time, new DifficultyControlPoint {
                        SliderVelocity = point.ScrollSpeed
                    });
                }
            }

            foreach (var group in legacyControlPoints.Groups)
            {
                var groupTimingPoint = group.ControlPoints.OfType <TimingControlPoint>().FirstOrDefault();

                // If the group contains a timing control point, it needs to be output separately.
                if (groupTimingPoint != null)
                {
                    writer.Write(FormattableString.Invariant($"{groupTimingPoint.Time},"));
                    writer.Write(FormattableString.Invariant($"{groupTimingPoint.BeatLength},"));
                    outputControlPointAt(groupTimingPoint.Time, true);
                }

                // Output any remaining effects as secondary non-timing control point.
                var difficultyPoint = legacyControlPoints.DifficultyPointAt(group.Time);
                writer.Write(FormattableString.Invariant($"{group.Time},"));
                writer.Write(FormattableString.Invariant($"{-100 / difficultyPoint.SliderVelocity},"));
                outputControlPointAt(group.Time, false);
            }

            void outputControlPointAt(double time, bool isTimingPoint)
            {
                var samplePoint = legacyControlPoints.SamplePointAt(time);
                var effectPoint = legacyControlPoints.EffectPointAt(time);

                // Apply the control point to a hit sample to uncover legacy properties (e.g. suffix)
                HitSampleInfo tempHitSample = samplePoint.ApplyTo(new ConvertHitObjectParser.LegacyHitSampleInfo(string.Empty));

                // Convert effect flags to the legacy format
                LegacyEffectFlags effectFlags = LegacyEffectFlags.None;

                if (effectPoint.KiaiMode)
                {
                    effectFlags |= LegacyEffectFlags.Kiai;
                }
                if (effectPoint.OmitFirstBarLine)
                {
                    effectFlags |= LegacyEffectFlags.OmitFirstBarLine;
                }

                writer.Write(FormattableString.Invariant($"{(int)legacyControlPoints.TimingPointAt(time).TimeSignature},"));
                writer.Write(FormattableString.Invariant($"{(int)toLegacySampleBank(tempHitSample.Bank)},"));
                writer.Write(FormattableString.Invariant($"{toLegacyCustomSampleBank(tempHitSample)},"));
                writer.Write(FormattableString.Invariant($"{tempHitSample.Volume},"));
                writer.Write(FormattableString.Invariant($"{(isTimingPoint ? '1' : '0')},"));
                writer.Write(FormattableString.Invariant($"{(int)effectFlags}"));
                writer.WriteLine();
            }

            IEnumerable <DifficultyControlPoint> collectDifficultyControlPoints(IEnumerable <HitObject> hitObjects)
            {
                if (!isOsuRuleset)
                {
                    yield break;
                }

                foreach (var hitObject in hitObjects)
                {
                    yield return(hitObject.DifficultyControlPoint);

                    foreach (var nested in collectDifficultyControlPoints(hitObject.NestedHitObjects))
                    {
                        yield return(nested);
                    }
                }
            }

            void extractDifficultyControlPoints(IEnumerable <HitObject> hitObjects)
            {
                foreach (var hDifficultyPoint in collectDifficultyControlPoints(hitObjects).OrderBy(dp => dp.Time))
                {
                    if (!hDifficultyPoint.IsRedundant(lastRelevantDifficultyPoint))
                    {
                        legacyControlPoints.Add(hDifficultyPoint.Time, hDifficultyPoint);
                        lastRelevantDifficultyPoint = hDifficultyPoint;
                    }
                }
            }

            IEnumerable <SampleControlPoint> collectSampleControlPoints(IEnumerable <HitObject> hitObjects)
            {
                foreach (var hitObject in hitObjects)
                {
                    yield return(hitObject.SampleControlPoint);

                    foreach (var nested in collectSampleControlPoints(hitObject.NestedHitObjects))
                    {
                        yield return(nested);
                    }
                }
            }

            void extractSampleControlPoints(IEnumerable <HitObject> hitObject)
            {
                foreach (var hSamplePoint in collectSampleControlPoints(hitObject).OrderBy(sp => sp.Time))
                {
                    if (!hSamplePoint.IsRedundant(lastRelevantSamplePoint))
                    {
                        legacyControlPoints.Add(hSamplePoint.Time, hSamplePoint);
                        lastRelevantSamplePoint = hSamplePoint;
                    }
                }
            }
        }
Beispiel #24
0
 /// <summary>
 /// Creates a <see cref="MultiplierControlPoint"/> by copying another <see cref="MultiplierControlPoint"/>.
 /// </summary>
 /// <param name="startTime">The start time of this <see cref="MultiplierControlPoint"/>.</param>
 /// <param name="other">The <see cref="MultiplierControlPoint"/> to copy.</param>
 public MultiplierControlPoint(double startTime, MultiplierControlPoint other)
     : this(startTime)
 {
     TimingPoint     = other.TimingPoint;
     DifficultyPoint = other.DifficultyPoint;
 }
Beispiel #25
0
        private void load()
        {
            double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
            double baseBeatLength = TimingControlPoint.DEFAULT_BEAT_LENGTH;

            if (RelativeScaleBeatLengths)
            {
                IReadOnlyList <TimingControlPoint> timingPoints = Beatmap.ControlPointInfo.TimingPoints;
                double maxDuration = 0;

                for (int i = 0; i < timingPoints.Count; i++)
                {
                    if (timingPoints[i].Time > lastObjectTime)
                    {
                        break;
                    }

                    double endTime  = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time : lastObjectTime;
                    double duration = endTime - timingPoints[i].Time;

                    if (duration > maxDuration)
                    {
                        maxDuration = duration;
                        // The slider multiplier is post-multiplied to determine the final velocity, but for relative scale beat lengths
                        // the multiplier should not affect the effective timing point (the longest in the beatmap), so it is factored out here
                        baseBeatLength = timingPoints[i].BeatLength / Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier;
                    }
                }
            }

            // Merge sequences of timing and difficulty control points to create the aggregate "multiplier" control point
            var lastTimingPoint     = new TimingControlPoint();
            var lastDifficultyPoint = new DifficultyControlPoint();
            var allPoints           = new SortedList <ControlPoint>(Comparer <ControlPoint> .Default);

            allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints);
            allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints);

            // Generate the timing points, making non-timing changes use the previous timing change and vice-versa
            var timingChanges = allPoints.Select(c =>
            {
                var timingPoint     = c as TimingControlPoint;
                var difficultyPoint = c as DifficultyControlPoint;

                if (timingPoint != null)
                {
                    lastTimingPoint = timingPoint;
                }

                if (difficultyPoint != null)
                {
                    lastDifficultyPoint = difficultyPoint;
                }

                return(new MultiplierControlPoint(c.Time)
                {
                    Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier,
                    BaseBeatLength = baseBeatLength,
                    TimingPoint = lastTimingPoint,
                    DifficultyPoint = lastDifficultyPoint
                });
            });

            // Trim unwanted sequences of timing changes
            timingChanges = timingChanges
                            // Collapse sections after the last hit object
                            .Where(s => s.StartTime <= lastObjectTime)
                            // Collapse sections with the same start time
                            .GroupBy(s => s.StartTime).Select(g => g.Last()).OrderBy(s => s.StartTime);

            controlPoints.AddRange(timingChanges);

            if (controlPoints.Count == 0)
            {
                controlPoints.Add(new MultiplierControlPoint {
                    Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier
                });
            }
        }
Beispiel #26
0
 public DifficultyPointPiece(DifficultyControlPoint difficultyPoint)
 {
     this.difficultyPoint = difficultyPoint;
     speedMultiplier      = difficultyPoint.SpeedMultiplierBindable.GetBoundCopy();
 }
Beispiel #27
0
 public DifficultyRowAttribute(DifficultyControlPoint difficulty)
     : base(difficulty, "difficulty")
 {
     speedMultiplier = difficulty.SpeedMultiplierBindable.GetBoundCopy();
 }
Beispiel #28
0
        protected override IEnumerable <BosuHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap)
        {
            var objPosition = (obj as IHasPosition)?.Position ?? Vector2.Zero;
            var comboData   = obj as IHasCombo;
            var difficulty  = beatmap.BeatmapInfo.BaseDifficulty;

            if (comboData?.NewCombo ?? false)
            {
                index++;
            }

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

            EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(obj.StartTime);
            bool kiai = effectPoint.KiaiMode;

            switch (obj)
            {
            // Slider
            case IHasCurve curve:
                if (!SlidersOnly)
                {
                    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 spanDuration         = curve.Duration / (curve.RepeatCount + 1);
                    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))
                    {
                        Vector2 sliderEventPosition;

                        // Don't take into account very small sliders. There's a chance that they will contain reverse spam, and offset looks ugly
                        if (spanDuration < 75)
                        {
                            sliderEventPosition = objPosition * new Vector2(1, 0.5f);
                        }
                        else
                        {
                            sliderEventPosition = (curve.CurvePositionAt(e.PathProgress / (curve.RepeatCount + 1)) + objPosition) * new Vector2(1, 0.5f);
                        }

                        switch (e.Type)
                        {
                        case SliderEventType.Head:
                            hitObjects.AddRange(generateExplosion(
                                                    e.Time,
                                                    kiai ? bullets_per_slider_head_kiai : bullets_per_slider_head,
                                                    sliderEventPosition,
                                                    comboData,
                                                    index));

                            if (Symmetry)
                            {
                                hitObjects.AddRange(generateExplosion(
                                                        e.Time,
                                                        kiai ? bullets_per_slider_head_kiai : bullets_per_slider_head,
                                                        getSymmetricalXPosition(sliderEventPosition),
                                                        comboData,
                                                        index));
                            }

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

                            break;

                        case SliderEventType.Tick:
                            hitObjects.Add(new TickCherry
                            {
                                StartTime      = e.Time,
                                Position       = sliderEventPosition,
                                NewCombo       = comboData?.NewCombo ?? false,
                                ComboOffset    = comboData?.ComboOffset ?? 0,
                                IndexInBeatmap = index
                            });

                            if (Symmetry)
                            {
                                hitObjects.Add(new TickCherry
                                {
                                    StartTime      = e.Time,
                                    Position       = getSymmetricalXPosition(sliderEventPosition),
                                    NewCombo       = comboData?.NewCombo ?? false,
                                    ComboOffset    = comboData?.ComboOffset ?? 0,
                                    IndexInBeatmap = index
                                });
                            }

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

                        case SliderEventType.Repeat:
                            hitObjects.AddRange(generateExplosion(
                                                    obj.StartTime + (e.SpanIndex + 1) * spanDuration,
                                                    kiai ? bullets_per_slider_reverse_kiai : bullets_per_slider_reverse,
                                                    sliderEventPosition,
                                                    comboData,
                                                    index,
                                                    slider_angle_per_span * e.SpanIndex));

                            if (Symmetry)
                            {
                                hitObjects.AddRange(generateExplosion(
                                                        obj.StartTime + (e.SpanIndex + 1) * spanDuration,
                                                        kiai ? bullets_per_slider_reverse_kiai : bullets_per_slider_reverse,
                                                        getSymmetricalXPosition(sliderEventPosition),
                                                        comboData,
                                                        index,
                                                        -slider_angle_per_span * e.SpanIndex));
                            }

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

                        case SliderEventType.Tail:
                            hitObjects.AddRange(generateExplosion(
                                                    e.Time,
                                                    kiai ? bullets_per_slider_tail_kiai : bullets_per_slider_tail,
                                                    sliderEventPosition,
                                                    comboData,
                                                    index));

                            if (Symmetry)
                            {
                                hitObjects.AddRange(generateExplosion(
                                                        e.Time,
                                                        kiai ? bullets_per_slider_tail_kiai : bullets_per_slider_tail,
                                                        getSymmetricalXPosition(sliderEventPosition),
                                                        comboData,
                                                        index));
                            }

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

                //body

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

                    if (!SlidersOnly)
                    {
                        position *= new Vector2(1, 0.5f);

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

                        if (Symmetry)
                        {
                            hitObjects.Add(new SliderPartCherry
                            {
                                StartTime      = obj.StartTime + curve.Duration * progress,
                                Position       = getSymmetricalXPosition(position),
                                NewCombo       = comboData?.NewCombo ?? false,
                                ComboOffset    = comboData?.ComboOffset ?? 0,
                                IndexInBeatmap = index
                            });
                        }
                    }
                    else
                    {
                        hitObjects.AddRange(new[]
                        {
                            new SliderPartCherry
                            {
                                StartTime      = obj.StartTime + curve.Duration * progress,
                                Position       = position,
                                NewCombo       = comboData?.NewCombo ?? false,
                                ComboOffset    = comboData?.ComboOffset ?? 0,
                                IndexInBeatmap = index
                            },
                            new SliderPartCherry
                            {
                                StartTime      = obj.StartTime + curve.Duration * progress,
                                Position       = getSymmetricalXPosition(position),
                                NewCombo       = comboData?.NewCombo ?? false,
                                ComboOffset    = comboData?.ComboOffset ?? 0,
                                IndexInBeatmap = index
                            },
                            new SliderPartCherry
                            {
                                StartTime      = obj.StartTime + curve.Duration * progress,
                                Position       = getSymmetricalYPosition(position),
                                NewCombo       = comboData?.NewCombo ?? false,
                                ComboOffset    = comboData?.ComboOffset ?? 0,
                                IndexInBeatmap = index
                            },
                            new SliderPartCherry
                            {
                                StartTime      = obj.StartTime + curve.Duration * progress,
                                Position       = getSymmetricalYPosition(getSymmetricalXPosition(position)),
                                NewCombo       = comboData?.NewCombo ?? false,
                                ComboOffset    = comboData?.ComboOffset ?? 0,
                                IndexInBeatmap = index
                            }
                        });
                    }
                }

                break;

            // Spinner
            case IHasEndTime endTime:
                if (SlidersOnly)
                {
                    break;
                }

                var spansPerSpinner = endTime.Duration / spinner_span_delay;

                for (int i = 0; i < spansPerSpinner; i++)
                {
                    hitObjects.AddRange(generateExplosion(
                                            obj.StartTime + i * spinner_span_delay,
                                            kiai ? bullets_per_spinner_span_kiai : bullets_per_spinner_span,
                                            objPosition * new Vector2(1, 0.5f),
                                            comboData,
                                            index,
                                            i * spinner_angle_per_span));
                }

                break;

            // Hitcircle
            default:
                if (SlidersOnly)
                {
                    hitObjects.AddRange(new[]
                    {
                        new SliderPartCherry
                        {
                            StartTime      = obj.StartTime,
                            Position       = objPosition,
                            NewCombo       = comboData?.NewCombo ?? false,
                            ComboOffset    = comboData?.ComboOffset ?? 0,
                            IndexInBeatmap = index
                        },
                        new SliderPartCherry
                        {
                            StartTime      = obj.StartTime,
                            Position       = getSymmetricalXPosition(objPosition),
                            NewCombo       = comboData?.NewCombo ?? false,
                            ComboOffset    = comboData?.ComboOffset ?? 0,
                            IndexInBeatmap = index
                        },
                        new SliderPartCherry
                        {
                            StartTime      = obj.StartTime,
                            Position       = getSymmetricalYPosition(objPosition),
                            NewCombo       = comboData?.NewCombo ?? false,
                            ComboOffset    = comboData?.ComboOffset ?? 0,
                            IndexInBeatmap = index
                        },
                        new SliderPartCherry
                        {
                            StartTime      = obj.StartTime,
                            Position       = getSymmetricalYPosition(getSymmetricalXPosition(objPosition)),
                            NewCombo       = comboData?.NewCombo ?? false,
                            ComboOffset    = comboData?.ComboOffset ?? 0,
                            IndexInBeatmap = index
                        }
                    });
                    break;
                }

                hitObjects.AddRange(generateExplosion(
                                        obj.StartTime,
                                        kiai ? bullets_per_hitcircle_kiai : bullets_per_hitcircle,
                                        objPosition * new Vector2(1, 0.5f),
                                        comboData,
                                        index,
                                        0,
                                        120));

                if (Symmetry)
                {
                    hitObjects.AddRange(generateExplosion(
                                            obj.StartTime,
                                            kiai ? bullets_per_hitcircle_kiai : bullets_per_hitcircle,
                                            getSymmetricalXPosition(objPosition * new Vector2(1, 0.5f)),
                                            comboData,
                                            index,
                                            0,
                                            120));
                }

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

                break;
            }

            return(hitObjects);
        }
        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);
        }
Beispiel #30
0
 public DifficultyEditPopover(HitObject hitObject)
 {
     this.hitObject = hitObject;
     point          = hitObject.DifficultyControlPoint;
 }