コード例 #1
0
        /// <summary>
        /// Applies the <see cref="Result"/> of this <see cref="DrawableHitObject"/>, notifying responders such as
        /// the <see cref="ScoreProcessor"/> of the <see cref="JudgementResult"/>.
        /// </summary>
        /// <param name="application">The callback that applies changes to the <see cref="JudgementResult"/>.</param>
        protected void ApplyResult(Action <JudgementResult> application)
        {
            if (Result.HasResult)
            {
                throw new InvalidOperationException("Cannot apply result on a hitobject that already has a result.");
            }

            application?.Invoke(Result);

            if (!Result.HasResult)
            {
                throw new InvalidOperationException($"{GetType().ReadableName()} applied a {nameof(JudgementResult)} but did not update {nameof(JudgementResult.Type)}.");
            }

            if (!Result.Type.IsValidHitResult(Result.Judgement.MinResult, Result.Judgement.MaxResult))
            {
                throw new InvalidOperationException(
                          $"{GetType().ReadableName()} applied an invalid hit result (was: {Result.Type}, expected: [{Result.Judgement.MinResult} ... {Result.Judgement.MaxResult}]).");
            }

            Result.TimeOffset = Math.Min(MaximumJudgementOffset, Time.Current - HitObject.GetEndTime());

            if (Result.HasResult)
            {
                updateState(Result.IsHit ? ArmedState.Hit : ArmedState.Miss);
            }

            OnNewResult?.Invoke(this, Result);
        }
コード例 #2
0
        private EdgeType getEdgeAtTime(HitObject hitObject, double time)
        {
            if (Precision.AlmostEquals(time, hitObject.StartTime, 1f))
            {
                return(EdgeType.Head);
            }
            if (Precision.AlmostEquals(time, hitObject.GetEndTime(), 1f))
            {
                return(EdgeType.Tail);
            }

            if (hitObject is IHasRepeats hasRepeats)
            {
                double spanDuration = hasRepeats.Duration / hasRepeats.SpanCount();
                if (spanDuration <= 0)
                {
                    // Prevents undefined behaviour in cases like where zero/negative-length sliders/hold notes exist.
                    return(EdgeType.None);
                }

                double spans = (time - hitObject.StartTime) / spanDuration;
                double acceptableDifference = 1 / spanDuration; // 1 ms of acceptable difference, as with head/tail above.

                if (Precision.AlmostEquals(spans, Math.Ceiling(spans), acceptableDifference) ||
                    Precision.AlmostEquals(spans, Math.Floor(spans), acceptableDifference))
                {
                    return(EdgeType.Repeat);
                }
            }

            return(EdgeType.None);
        }
コード例 #3
0
        protected override void Update()
        {
            base.Update();

            // no bindable so we perform this every update
            Width = (float)(HitObject.GetEndTime() - HitObject.StartTime);
        }
コード例 #4
0
ファイル: ModTimeRamp.cs プロジェクト: gh-matv/osu
        public virtual void ApplyToBeatmap(IBeatmap beatmap)
        {
            HitObject lastObject = beatmap.HitObjects.LastOrDefault();

            beginRampTime = beatmap.HitObjects.FirstOrDefault()?.StartTime ?? 0;
            finalRateTime = final_rate_progress * (lastObject?.GetEndTime() ?? 0);
        }
コード例 #5
0
ファイル: DifficultyHitObject.cs プロジェクト: shadiwolf/e
 /// <summary>
 /// Creates a new <see cref="DifficultyHitObject"/>.
 /// </summary>
 /// <param name="hitObject">The <see cref="HitObject"/> which this <see cref="DifficultyHitObject"/> wraps.</param>
 /// <param name="lastObject">The last <see cref="HitObject"/> which occurs before <paramref name="hitObject"/> in the beatmap.</param>
 /// <param name="clockRate">The rate at which the gameplay clock is run at.</param>
 public DifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate)
 {
     BaseObject = hitObject;
     LastObject = lastObject;
     DeltaTime = (hitObject.StartTime - lastObject.StartTime) / clockRate;
     StartTime = hitObject.StartTime / clockRate;
     EndTime = hitObject.GetEndTime() / clockRate;
 }
コード例 #6
0
ファイル: DrawableDrumRollTick.cs プロジェクト: Wieku/osu
        public override void OnKilled()
        {
            base.OnKilled();

            if (Time.Current > HitObject.GetEndTime() && !Judged)
            {
                ApplyResult(r => r.Type = r.Judgement.MinResult);
            }
        }
コード例 #7
0
 protected override IEnumerable <ConvertValue> CreateConvertValue(HitObject hitObject)
 {
     yield return(new ConvertValue
     {
         StartTime = hitObject.StartTime,
         EndTime = hitObject.GetEndTime(),
         Column = ((ManiaHitObject)hitObject).Column
     });
 }
コード例 #8
0
 protected override IEnumerable <SampleConvertValue> CreateConvertValue(HitObject hitObject)
 {
     yield return(new SampleConvertValue
     {
         StartTime = hitObject.StartTime,
         EndTime = hitObject.GetEndTime(),
         Column = ((ManiaHitObject)hitObject).Column,
         NodeSamples = getSampleNames((hitObject as HoldNote)?.NodeSamples)
     });
 }
コード例 #9
0
ファイル: DifficultyHitObject.cs プロジェクト: Wieku/osu
 public DifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, List <DifficultyHitObject> objects, int index)
 {
     difficultyHitObjects = objects;
     Index      = index;
     BaseObject = hitObject;
     LastObject = lastObject;
     DeltaTime  = (hitObject.StartTime - lastObject.StartTime) / clockRate;
     StartTime  = hitObject.StartTime / clockRate;
     EndTime    = hitObject.GetEndTime() / clockRate;
 }
コード例 #10
0
        protected override void Update()
        {
            base.Update();

            isHitting.Value = Time.Current >= HitObject.StartTime
                            && Time.Current <= HitObject.GetEndTime()
                            && (Auto || checkForTouchInput() || ((SentakkiActionInputManager?.PressedActions.Any() ?? false) && IsHovered));

            if (isHitting.Value)
                holdSample.Frequency.Value = 0.5 + ((Time.Current - holdStartTime.Value + totalHoldTime) / ((IHasDuration)HitObject).Duration);
        }
コード例 #11
0
ファイル: OsuHitObjectComposer.cs プロジェクト: esgloamp/osu
        /// <summary>
        /// Creates a grid from the last <see cref="HitObject"/> matching a predicate to a target <see cref="HitObject"/>.
        /// </summary>
        /// <param name="sourceSelector">A predicate that matches <see cref="HitObject"/>s where the grid can start from.
        /// Only the last <see cref="HitObject"/> matching the predicate is used.</param>
        /// <param name="targetOffset">An offset from the <see cref="HitObject"/> selected via <paramref name="sourceSelector"/> at which the grid should stop.</param>
        /// <returns>The <see cref="OsuDistanceSnapGrid"/> from a selected <see cref="HitObject"/> to a target <see cref="HitObject"/>.</returns>
        private OsuDistanceSnapGrid createGrid(Func <HitObject, bool> sourceSelector, int targetOffset = 1)
        {
            if (targetOffset < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(targetOffset));
            }

            int sourceIndex = -1;

            for (int i = 0; i < EditorBeatmap.HitObjects.Count; i++)
            {
                if (!sourceSelector(EditorBeatmap.HitObjects[i]))
                {
                    break;
                }

                sourceIndex = i;
            }

            if (sourceIndex == -1)
            {
                return(null);
            }

            HitObject sourceObject = EditorBeatmap.HitObjects[sourceIndex];

            int       targetIndex  = sourceIndex + targetOffset;
            HitObject targetObject = null;

            // Keep advancing the target object while its start time falls before the end time of the source object
            while (true)
            {
                if (targetIndex >= EditorBeatmap.HitObjects.Count)
                {
                    break;
                }

                if (EditorBeatmap.HitObjects[targetIndex].StartTime >= sourceObject.GetEndTime())
                {
                    targetObject = EditorBeatmap.HitObjects[targetIndex];
                    break;
                }

                targetIndex++;
            }

            if (sourceObject is Spinner)
            {
                return(null);
            }

            return(new OsuDistanceSnapGrid((OsuHitObject)sourceObject, (OsuHitObject)targetObject));
        }
コード例 #12
0
        protected override IEnumerable<SentakkiHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap)
        {
            Vector2 CENTRE_POINT = new Vector2(256, 192);
            Vector2 newPos = (original as IHasPosition)?.Position ?? Vector2.Zero;
            newPos.Y = 384 - newPos.Y;
            float angle = Utils.GetNotePathFromDegrees(Utils.GetDegreesFromPosition(newPos, CENTRE_POINT));

            switch (original)
            {
                case IHasCurve curveData:
                    return new Hold
                    {
                        NoteColor = Color4.Crimson,
                        Angle = Utils.GetNotePathFromDegrees(Utils.GetDegreesFromPosition(newPos, CENTRE_POINT)),
                        NodeSamples = curveData.NodeSamples,
                        StartTime = original.StartTime,
                        EndTime = original.GetEndTime(),
                        endPosition = new Vector2(-(SentakkiPlayfield.IntersectDistance * (float)Math.Cos((angle + 90f) * (float)(Math.PI / 180))), -(SentakkiPlayfield.IntersectDistance * (float)Math.Sin((angle + 90f) * (float)(Math.PI / 180)))),
                        Position = new Vector2(-(SentakkiPlayfield.NoteStartDistance * (float)Math.Cos((angle + 90f) * (float)(Math.PI / 180))), -(SentakkiPlayfield.NoteStartDistance * (float)Math.Sin((angle + 90f) * (float)(Math.PI / 180)))),
                    }.Yield();

                case IHasEndTime endTimeData:
                    return new TouchHold
                    {
                        Position = Vector2.Zero,
                        StartTime = original.StartTime,
                        EndTime = endTimeData.EndTime,
                    }.Yield();

                default:
                    bool strong = original.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
                    if (strong)
                        return new Break
                        {
                            NoteColor = Color4.OrangeRed,
                            Angle = Utils.GetNotePathFromDegrees(Utils.GetDegreesFromPosition(newPos, CENTRE_POINT)),
                            Samples = original.Samples,
                            StartTime = original.StartTime,
                            endPosition = new Vector2(-(SentakkiPlayfield.IntersectDistance * (float)Math.Cos((angle + 90f) * (float)(Math.PI / 180))), -(SentakkiPlayfield.IntersectDistance * (float)Math.Sin((angle + 90f) * (float)(Math.PI / 180)))),
                            Position = new Vector2(-(SentakkiPlayfield.NoteStartDistance * (float)Math.Cos((angle + 90f) * (float)(Math.PI / 180))), -(SentakkiPlayfield.NoteStartDistance * (float)Math.Sin((angle + 90f) * (float)(Math.PI / 180)))),
                        }.Yield();
                    else
                        return new Tap
                        {
                            NoteColor = Color4.Orange,
                            Angle = Utils.GetNotePathFromDegrees(Utils.GetDegreesFromPosition(newPos, CENTRE_POINT)),
                            Samples = original.Samples,
                            StartTime = original.StartTime,
                            endPosition = new Vector2(-(SentakkiPlayfield.IntersectDistance * (float)Math.Cos((angle + 90f) * (float)(Math.PI / 180))), -(SentakkiPlayfield.IntersectDistance * (float)Math.Sin((angle + 90f) * (float)(Math.PI / 180)))),
                            Position = new Vector2(-(SentakkiPlayfield.NoteStartDistance * (float)Math.Cos((angle + 90f) * (float)(Math.PI / 180))), -(SentakkiPlayfield.NoteStartDistance * (float)Math.Sin((angle + 90f) * (float)(Math.PI / 180)))),
                        }.Yield();
            }
        }
コード例 #13
0
        private double calculateReleaseTime(HitObject currentObject, HitObject nextObject)
        {
            double endTime = currentObject.GetEndTime();

            if (currentObject is HoldNote)
                // hold note releases must be timed exactly.
                return endTime;

            bool canDelayKeyUpFully = nextObject == null ||
                                      nextObject.StartTime > endTime + RELEASE_DELAY;

            return endTime + (canDelayKeyUpFully ? RELEASE_DELAY : (nextObject.StartTime - endTime) * 0.9);
        }
コード例 #14
0
        /// <summary>
        /// Processes this <see cref="DrawableHitObject"/>, checking if a scoring result has occurred.
        /// </summary>
        /// <param name="userTriggered">Whether the user triggered this process.</param>
        /// <returns>Whether a scoring result has occurred from this <see cref="DrawableHitObject"/> or any nested <see cref="DrawableHitObject"/>.</returns>
        protected bool UpdateResult(bool userTriggered)
        {
            // It's possible for input to get into a bad state when rewinding gameplay, so results should not be processed
            if (Time.Elapsed < 0)
                return false;

            if (Judged)
                return false;

            CheckForResult(userTriggered, Time.Current - HitObject.GetEndTime());

            return Judged;
        }
コード例 #15
0
 protected override IEnumerable <ConvertValue> CreateConvertValue(HitObject hitObject)
 {
     yield return(new ConvertValue
     {
         StartTime = hitObject.StartTime,
         EndTime = hitObject.GetEndTime(),
         IsRim = (hitObject as Hit)?.Type == HitType.Rim,
         IsCentre = (hitObject as Hit)?.Type == HitType.Centre,
         IsDrumRoll = hitObject is DrumRoll,
         IsSwell = hitObject is Swell,
         IsStrong = ((TaikoHitObject)hitObject).IsStrong
     });
 }
コード例 #16
0
        private SentakkiHitObject createHoldNote(HitObject original, bool twin = false, bool isBreak = false)
        {
            int noteLane = getNewLane(twin);

            return(new Hold
            {
                Break = isBreak,
                Lane = noteLane,
                NodeSamples = (original as IHasPathWithRepeats).NodeSamples,
                StartTime = original.StartTime,
                EndTime = original.GetEndTime()
            });
        }
コード例 #17
0
ファイル: HitObjectComposer.cs プロジェクト: gpsbird/osu-1
        public void EndPlacement(HitObject hitObject, bool commit)
        {
            EditorBeatmap.PlacementObject.Value = null;

            if (commit)
            {
                EditorBeatmap.Add(hitObject);

                adjustableClock.Seek(hitObject.GetEndTime());
            }

            showGridFor(Enumerable.Empty <HitObject>());
        }
コード例 #18
0
        private void convertToStream()
        {
            if (editorBeatmap == null || changeHandler == null || beatDivisor == null)
            {
                return;
            }

            var    timingPoint   = editorBeatmap.ControlPointInfo.TimingPointAt(HitObject.StartTime);
            double streamSpacing = timingPoint.BeatLength / beatDivisor.Value;

            changeHandler.BeginChange();

            int    i    = 0;
            double time = HitObject.StartTime;

            while (!Precision.DefinitelyBigger(time, HitObject.GetEndTime(), 1))
            {
                // positionWithRepeats is a fractional number in the range of [0, HitObject.SpanCount()]
                // and indicates how many fractional spans of a slider have passed up to time.
                double positionWithRepeats = (time - HitObject.StartTime) / HitObject.Duration * HitObject.SpanCount();
                double pathPosition        = positionWithRepeats - (int)positionWithRepeats;
                // every second span is in the reverse direction - need to reverse the path position.
                if (Precision.AlmostBigger(positionWithRepeats % 2, 1))
                {
                    pathPosition = 1 - pathPosition;
                }

                Vector2 position = HitObject.Position + HitObject.Path.PositionAt(pathPosition);

                var samplePoint = (SampleControlPoint)HitObject.SampleControlPoint.DeepClone();
                samplePoint.Time = time;

                editorBeatmap.Add(new HitCircle
                {
                    StartTime          = time,
                    Position           = position,
                    NewCombo           = i == 0 && HitObject.NewCombo,
                    SampleControlPoint = samplePoint,
                    Samples            = HitObject.HeadCircle.Samples.Select(s => s.With()).ToList()
                });

                i   += 1;
                time = HitObject.StartTime + i * streamSpacing;
            }

            editorBeatmap.Remove(HitObject);

            changeHandler.EndChange();
        }
コード例 #19
0
        protected override void OnApply()
        {
            base.OnApply();

            IndexInCurrentComboBindable.BindTo(HitObject.IndexInCurrentComboBindable);
            PositionBindable.BindTo(HitObject.PositionBindable);
            StackHeightBindable.BindTo(HitObject.StackHeightBindable);
            ScaleBindable.BindTo(HitObject.ScaleBindable);

            // Manually set to reduce the number of future alive objects to a bare minimum.
            LifetimeStart = HitObject.StartTime - HitObject.TimePreempt;

            // Arbitrary lifetime end to prevent past objects in idle states remaining alive in non-frame-stable contexts.
            // An extra 1000ms is added to always overestimate the true lifetime, and a more exact value is set by hit transforms and the following expiry.
            LifetimeEnd = HitObject.GetEndTime() + HitObject.HitWindows.WindowFor(HitResult.Miss) + 1000;
        }
コード例 #20
0
        // TODO: add tests for this
        //public void Test(string name) => base.Test(name);

        protected override IEnumerable <ConvertValue> CreateConvertValue(HitObject hitObject)
        {
            yield return(new ConvertValue
            {
                StartTime = hitObject.StartTime,
                EndTime = hitObject.GetEndTime(),
                Lane = (hitObject as LanedHit)?.Lane,
                IsMinion = hitObject is Minion,
                IsNoteSheet = hitObject is NoteSheet,
                IsMiniBoss = hitObject is MiniBoss,
                IsSawblade = hitObject is Sawblade,
                HasHeart = hitObject is Heart /*|| (hitObject is LanedHit)?.HasHeart*/,
                IsHammer = false, // TODO
                IsNote = false,   // TODO
            });
        }
コード例 #21
0
        protected override void Update()
        {
            base.Update();

            // no bindable so we perform this every update
            float duration = (float)(HitObject.GetEndTime() - HitObject.StartTime);

            if (Width != duration)
            {
                Width = duration;

                // kind of haphazard but yeah, no bindables.
                if (HitObject is IHasRepeats repeats)
                {
                    updateRepeats(repeats);
                }
            }
        }
コード例 #22
0
            public TimelineHitObjectRepresentation(HitObject hitObject)
            {
                HitObject = hitObject;
                Anchor    = Anchor.CentreLeft;
                Origin    = Anchor.CentreLeft;

                Width = (float)(hitObject.GetEndTime() - hitObject.StartTime);

                X = (float)hitObject.StartTime;

                RelativePositionAxes = Axes.X;
                RelativeSizeAxes     = Axes.X;

                if (hitObject is IHasEndTime)
                {
                    AddInternal(new Container
                    {
                        CornerRadius         = 2,
                        Masking              = true,
                        Size                 = new Vector2(1, THICKNESS),
                        Anchor               = Anchor.CentreLeft,
                        Origin               = Anchor.CentreLeft,
                        RelativePositionAxes = Axes.X,
                        RelativeSizeAxes     = Axes.X,
                        Colour               = Color4.Black,
                        Child                = new Box
                        {
                            RelativeSizeAxes = Axes.Both,
                        }
                    });
                }

                AddInternal(new Circle
                {
                    Size   = new Vector2(16),
                    Anchor = Anchor.CentreLeft,
                    Origin = Anchor.Centre,
                    RelativePositionAxes = Axes.X,
                    AlwaysPresent        = true,
                    Colour          = Color4.White,
                    BorderColour    = Color4.Black,
                    BorderThickness = THICKNESS,
                });
            }
コード例 #23
0
        protected override void Update()
        {
            base.Update();

            if (Result != null && Result.HasResult)
            {
                var endTime = HitObject.GetEndTime();

                if (Result.TimeOffset + endTime > Time.Current)
                {
                    OnRevertResult?.Invoke(this, Result);

                    Result.TimeOffset = 0;
                    Result.Type       = HitResult.None;

                    updateState(ArmedState.Idle);
                }
            }
        }
コード例 #24
0
            protected override void Update()
            {
                base.Update();

                if (editorClock == null)
                {
                    return;
                }

                float  distanceSpacingMultiplier = (float)snapProvider.DistanceSpacingMultiplier.Value;
                double timeFromReferencePoint    = editorClock.CurrentTime - referenceObject.GetEndTime();

                float distanceForCurrentTime = snapProvider.DurationToDistance(referenceObject, timeFromReferencePoint)
                                               * distanceSpacingMultiplier;

                float timeBasedAlpha = 1 - Math.Clamp(Math.Abs(distanceForCurrentTime - Size.X / 2) / 30, 0, 1);

                Colour = baseColour.Opacity(Math.Max(baseColour.A, timeBasedAlpha));
            }
コード例 #25
0
        public override IEnumerable <Issue> GetIssues(Beatmap beatmap)
        {
            int hitObjectCount = beatmap.hitObjects.Count();

            for (int i = 0; i < hitObjectCount - 1; ++i)
            {
                for (int j = i + 1; j < hitObjectCount; ++j)
                {
                    HitObject hitObject      = beatmap.hitObjects[i];
                    HitObject otherHitObject = beatmap.hitObjects[j];

                    if (beatmap.generalSettings.mode == Beatmap.Mode.Mania &&
                        hitObject.Position.X != otherHitObject.Position.X)
                    {
                        continue;
                    }

                    // Only need to check forwards, as any previous object will already have looked behind this one.
                    double msApart = otherHitObject.time - hitObject.GetEndTime();

                    if (msApart <= 0)
                    {
                        yield return(new Issue(GetTemplate("Concurrent Objects"), beatmap,
                                               Timestamp.Get(hitObject, otherHitObject), ObjectsAsString(hitObject, otherHitObject)));
                    }

                    else if (msApart <= 10)
                    {
                        yield return(new Issue(GetTemplate("Almost Concurrent Objects"), beatmap,
                                               Timestamp.Get(hitObject, otherHitObject), msApart));
                    }

                    else
                    {
                        // Hit objects are sorted by time, meaning if the next one is > 10 ms away, any remaining will be too.
                        break;
                    }
                }
            }
        }
コード例 #26
0
        private IEnumerable <Issue> getVolumeIssues(HitObject hitObject, HitObject sampledHitObject = null)
        {
            sampledHitObject ??= hitObject;
            if (!sampledHitObject.Samples.Any())
            {
                yield break;
            }

            // Samples that allow themselves to be overridden by control points have a volume of 0.
            int    maxVolume      = sampledHitObject.Samples.Max(sample => sample.Volume > 0 ? sample.Volume : sampledHitObject.SampleControlPoint.SampleVolume);
            double samplePlayTime = sampledHitObject.GetEndTime();

            EdgeType edgeType = getEdgeAtTime(hitObject, samplePlayTime);

            // We only care about samples played on the edges of objects, not ones like spinnerspin or slidertick.
            if (edgeType == EdgeType.None)
            {
                yield break;
            }

            string postfix = hitObject is IHasDuration?edgeType.ToString().ToLower() : null;

            if (maxVolume <= muted_threshold)
            {
                if (edgeType == EdgeType.Head)
                {
                    yield return(new IssueTemplateMutedActive(this).Create(hitObject, maxVolume / 100f, sampledHitObject.GetEndTime(), postfix));
                }
                else
                {
                    yield return(new IssueTemplateMutedPassive(this).Create(hitObject, maxVolume / 100f, sampledHitObject.GetEndTime(), postfix));
                }
            }
            else if (maxVolume <= low_volume_threshold && edgeType == EdgeType.Head)
            {
                yield return(new IssueTemplateLowVolumeActive(this).Create(hitObject, maxVolume / 100f, sampledHitObject.GetEndTime(), postfix));
            }
        }
コード例 #27
0
        protected override void UpdateHitStateTransforms(ArmedState state)
        {
            switch (state)
            {
            case ArmedState.Miss:
                AccentColour.Value = Color4.Gray;

                if (!ExpireOnMiss)
                {
                    LifetimeEnd = HitObject.GetEndTime() + LifetimeEndDelay;
                }

                break;

            case ArmedState.Hit:
                if (!ExpireOnHit)
                {
                    LifetimeEnd = HitObject.GetEndTime() + LifetimeEndDelay;
                }

                break;
            }
        }
コード例 #28
0
        /// <summary>
        /// Handles a request for range selection (triggered when Shift is held down).
        /// </summary>
        /// <param name="blueprint">The blueprint which was clicked in range selection mode.</param>
        /// <param name="cumulative">
        /// Whether the selection should be cumulative.
        /// In cumulative mode, consecutive range selections will shift the pivot (which usually stays fixed for the duration of a range selection)
        /// and will never deselect an object that was previously selected.
        /// </param>
        private void handleRangeSelection(SelectionBlueprint <HitObject> blueprint, bool cumulative)
        {
            var clickedObject = blueprint.Item;

            Debug.Assert(pivot != null);

            double rangeStart = Math.Min(clickedObject.StartTime, pivot.StartTime);
            double rangeEnd   = Math.Max(clickedObject.GetEndTime(), pivot.GetEndTime());

            var newSelection = new HashSet <HitObject>(EditorBeatmap.HitObjects.Where(obj => isInRange(obj, rangeStart, rangeEnd)));

            if (cumulative)
            {
                pivot = clickedObject;
                newSelection.UnionWith(EditorBeatmap.SelectedHitObjects);
            }

            EditorBeatmap.SelectedHitObjects.Clear();
            EditorBeatmap.SelectedHitObjects.AddRange(newSelection);

            bool isInRange(HitObject hitObject, double start, double end)
            => hitObject.StartTime >= start && hitObject.GetEndTime() <= end;
        }
コード例 #29
0
        private IEnumerable <Issue> applyHitsoundUpdate(HitObject hitObject, bool isLastObject = false)
        {
            double time              = hitObject.GetEndTime();
            bool   hasHitsound       = hitObject.Samples.Any(isHitsound);
            bool   couldHaveHitsound = hitObject.Samples.Any(isHitnormal);

            // Only generating issues on hitsounded or last objects ensures we get one issue per long period.
            // If there are no hitsounds we let the "No hitsounds" template take precedence.
            if (hasHitsound || (isLastObject && mapHasHitsounds))
            {
                double timeWithoutHitsounds = time - lastHitsoundTime;

                if (timeWithoutHitsounds > problem_threshold_time && objectsWithoutHitsounds > problem_threshold_objects)
                {
                    yield return(new IssueTemplateLongPeriodProblem(this).Create(lastHitsoundTime, timeWithoutHitsounds));
                }
                else if (timeWithoutHitsounds > warning_threshold_time && objectsWithoutHitsounds > warning_threshold_objects)
                {
                    yield return(new IssueTemplateLongPeriodWarning(this).Create(lastHitsoundTime, timeWithoutHitsounds));
                }
                else if (timeWithoutHitsounds > negligible_threshold_time && objectsWithoutHitsounds > warning_threshold_objects)
                {
                    yield return(new IssueTemplateLongPeriodNegligible(this).Create(lastHitsoundTime, timeWithoutHitsounds));
                }
            }

            if (hasHitsound)
            {
                mapHasHitsounds         = true;
                objectsWithoutHitsounds = 0;
                lastHitsoundTime        = time;
            }
            else if (couldHaveHitsound)
            {
                ++objectsWithoutHitsounds;
            }
        }
コード例 #30
0
ファイル: CheckSpinnerGap.cs プロジェクト: rorre/CatchCheck
        public override IEnumerable <Issue> GetIssues(Beatmap aBeatmap)
        {
            HitObject hitObject           = default(HitObject);
            float     requirePreviousTime = 0.0f;
            float     requireNextTime     = 0.0f;

            for (var i = 0; i < aBeatmap.hitObjects.Count; i++)
            {
                var nextTime     = 0.0;
                var previousTime = 0.0;
                hitObject = aBeatmap.hitObjects[i];

                if (hitObject is Spinner)
                {
                    bool isPreviousSpinner = false;
                    bool isNextSpinner     = false;

                    if (i - 1 >= 0)
                    {
                        var previousObject = aBeatmap.hitObjects[i - 1];
                        previousTime      = hitObject.time - previousObject.time;
                        isPreviousSpinner = previousObject is Spinner;
                    }

                    if (i + 1 < aBeatmap.hitObjects.Count)
                    {
                        var nextObject = aBeatmap.hitObjects[i + 1];
                        nextTime      = nextObject.time - hitObject.GetEndTime();
                        isNextSpinner = nextObject is Spinner;
                    }

                    if (previousTime < 62 && previousTime != 0.0)
                    {
                        if (isPreviousSpinner)
                        {
                            yield return(new Issue(
                                             GetTemplate(GetKind(isPreviousSpinner) + " Previous"),
                                             aBeatmap,
                                             Timestamp.Get(hitObject),
                                             $"{(int)previousTime}",
                                             $"{(int)requirePreviousTime}"
                                             ).ForDifficulties(
                                             Beatmap.Difficulty.Expert,
                                             Beatmap.Difficulty.Ultra
                                             ));
                        }
                    }

                    if (previousTime < 125 && previousTime != 0.0)
                    {
                        yield return(new Issue(
                                         GetTemplate(GetKind(isPreviousSpinner) + " Previous"),
                                         aBeatmap,
                                         Timestamp.Get(hitObject),
                                         $"{(int)previousTime}",
                                         $"{(int)requirePreviousTime}"
                                         ).ForDifficulties(
                                         Beatmap.Difficulty.Easy,
                                         Beatmap.Difficulty.Normal,
                                         Beatmap.Difficulty.Hard,
                                         Beatmap.Difficulty.Insane
                                         ));
                    }

                    if (nextTime < 125 && nextTime != 0.0)
                    {
                        yield return(new Issue(
                                         GetTemplate(GetKind(isNextSpinner) + " Next"),
                                         aBeatmap,
                                         Timestamp.Get(hitObject),
                                         $"{(int)nextTime}",
                                         $"{(int)requireNextTime}"
                                         ).ForDifficulties(
                                         Beatmap.Difficulty.Insane,
                                         Beatmap.Difficulty.Expert,
                                         Beatmap.Difficulty.Ultra
                                         ));
                    }

                    if (nextTime < 250 && nextTime != 0.0)
                    {
                        yield return(new Issue(
                                         GetTemplate(GetKind(isNextSpinner) + " Next"),
                                         aBeatmap,
                                         Timestamp.Get(hitObject),
                                         $"{(int)nextTime}",
                                         $"{(int)requireNextTime}"
                                         ).ForDifficulties(
                                         Beatmap.Difficulty.Easy,
                                         Beatmap.Difficulty.Normal,
                                         Beatmap.Difficulty.Hard
                                         ));
                    }
                }
            }
        }