/// <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); }
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); }
protected override void Update() { base.Update(); // no bindable so we perform this every update Width = (float)(HitObject.GetEndTime() - HitObject.StartTime); }
public virtual void ApplyToBeatmap(IBeatmap beatmap) { HitObject lastObject = beatmap.HitObjects.LastOrDefault(); beginRampTime = beatmap.HitObjects.FirstOrDefault()?.StartTime ?? 0; finalRateTime = final_rate_progress * (lastObject?.GetEndTime() ?? 0); }
/// <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; }
public override void OnKilled() { base.OnKilled(); if (Time.Current > HitObject.GetEndTime() && !Judged) { ApplyResult(r => r.Type = r.Judgement.MinResult); } }
protected override IEnumerable <ConvertValue> CreateConvertValue(HitObject hitObject) { yield return(new ConvertValue { StartTime = hitObject.StartTime, EndTime = hitObject.GetEndTime(), Column = ((ManiaHitObject)hitObject).Column }); }
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) }); }
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; }
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); }
/// <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)); }
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(); } }
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); }
/// <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; }
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 }); }
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() }); }
public void EndPlacement(HitObject hitObject, bool commit) { EditorBeatmap.PlacementObject.Value = null; if (commit) { EditorBeatmap.Add(hitObject); adjustableClock.Seek(hitObject.GetEndTime()); } showGridFor(Enumerable.Empty <HitObject>()); }
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(); }
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; }
// 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 }); }
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); } } }
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, }); }
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); } } }
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)); }
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; } } } }
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)); } }
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; } }
/// <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; }
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; } }
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 )); } } } }