protected virtual void runComposeRules() { circleRadius = (float)(512 / 8f * (1f - 0.7f * hitObjectManager.AdjustDifficulty(BeatmapManager.Current.DifficultyCircleSize)) / 2f) - 1; runSanityRules(); List <HitObjectBase> hitObjects = hitObjectManager.GetHitObjects(); bool lastWasSpinner = false; double slowestBeatlength = 0; for (int i = 0; i < hitObjects.Count; i++) { bool isLast = i == hitObjects.Count - 1; HitObjectBase h = hitObjects[i]; HitObjectBase h1 = isLast ? null : hitObjects[i + 1]; //check offscreen if (testOffScreen(h.Position)) { Reports.Add(new AiReportOneObject(h, h.StartTime, delegate { return(testOffScreen(h.Position)); }, Severity.Warning, LocalisationManager.GetString(OsuString.AICompose_ObjectOffscreen), 0)); } if (h.EndPosition != h.Position && testOffScreen(h.EndPosition)) { Reports.Add(new AiReportOneObject(h, h.EndTime, delegate { return(testOffScreen(h.EndPosition)); }, Severity.Warning, LocalisationManager.GetString(OsuString.AICompose_ObjectEndOffscreen), 0)); } //Check for combos over 24 if (h.ComboNumber > 24) { Reports.Add(new AiReportOneObject(h, h.EndTime, delegate { return(h.ComboNumber <= 24); }, Severity.Warning, LocalisationManager.GetString(OsuString.AICompose_LongCombo), -1)); } if (i > 0 && !isLast) { if (hitObjects[i].StartTime == hitObjects[i + 1].StartTime) { HitObjectBase last = hitObjects[i - 1]; Reports.Add(new AiReportTwoObjects(last, h, delegate { return(last.StartTime != h.StartTime); }, Severity.Error, LocalisationManager.GetString(OsuString.AICompose_SimultaneousObjects), -1)); } } if (lastWasSpinner && !(h is Spinner)) { Spinner spinner = (Spinner)hitObjects[i - 1]; if (spinner.StartTime > h.StartTime - hitObjectManager.PreEmpt + HitObjectManager.FadeIn) { Reports.Add(new AiReportTwoObjects(spinner, h, delegate() { return(spinner.StartTime <= h.StartTime - hitObjectManager.PreEmpt + HitObjectManager.FadeIn); }, Severity.Error, LocalisationManager.GetString(OsuString.AICompose_NinjaSpinner), -1)); } } lastWasSpinner = (h is Spinner); if (h is SliderOsu) { // curve stuff is only defined for osu!mode, but happily enough, the editor is always in osu!mode. SliderOsu s2 = h as SliderOsu; if (s2.IsRetarded()) { Reports.Add(new AiReportOneObject(h, h.StartTime, null, Severity.Error, LocalisationManager.GetString(OsuString.AICompose_AbnormalSlider), -1)); } // if the mapper sets a broken tick rate but never has long enough sliders to notice it, don't warn if (s2.EndTime - 1000 > s2.StartTime) { slowestBeatlength = Math.Max(slowestBeatlength, AudioEngine.BeatLengthAt(s2.StartTime)); } } //spinner length if (h.IsType(HitObjectType.Spinner)) { if (!h.IsType(HitObjectType.NewCombo)) { Reports.Add(new AiReportOneObject(h, h.StartTime, null, Severity.Warning, LocalisationManager.GetString(OsuString.AICompose_SpinnerNoNewCombo), -1)); } //look at SpinnerOsu.cs for these numbers. double autoCanSpin = 0.05 * (double)(h.EndTime - h.StartTime) / Math.PI; double require = ((double)(h.EndTime - h.StartTime) / 1000 * hitObjectManager.SpinnerRotationRatio); if (autoCanSpin - require < 5) { Reports.Add(new AiReportOneObject(h, h.StartTime, null, Severity.Warning, LocalisationManager.GetString(OsuString.AICompose_ShortSpinner), -1)); } } checkDistanceSpacing(h, h1, hitObjectManager); } }
protected virtual void runTimingRules() { List <HitObjectBase> hitObjects = hitObjectManager.GetHitObjects(); int mapEndTime = 0; int drainLength = 0; if (hitObjects.Count > 0) { mapEndTime = hitObjects[hitObjects.Count - 1].EndTime; drainLength = mapEndTime - hitObjects[0].StartTime; if (drainLength > 6 * 60 * 1000) { Reports.Add(new AiReport(-1, Severity.Warning, LocalisationManager.GetString(OsuString.AITiming_BeatmapTooLong), -1, null)); } else if (drainLength < 45000) { Reports.Add(new AiReport(-1, Severity.Warning, LocalisationManager.GetString(OsuString.AITiming_BeatmapTooShort), -1, null)); } if (drainLength * 1.0 / (AudioEngine.AudioLength - hitObjects[0].StartTime) < 0.8) { Reports.Add(new AiReport(-1, Severity.Warning, LocalisationManager.GetString(OsuString.AITiming_Mp3LongerThanMap), -1, null)); } } List <ControlPoint> points = AudioEngine.ControlPoints; int kiaiToggleCount = 0; int kiaiLength = 0; List <float> SliderMultipliers = new List <float>(); bool kiaiOn = false; double lastKiaiEndTime = 0; int lowVolumeCount = 0; int stackedPointCount = 0; for (int i = 0; i < points.Count; i++) { ControlPoint p = points[i]; //overlap int startOffset = (int)p.Offset; int endOffset = i == points.Count - 1 ? mapEndTime : (int)points[i + 1].Offset; int timingLength = endOffset - startOffset; if (timingLength > 0) { if (p.KiaiMode) { kiaiLength += timingLength; if (i > 0 && !points[i - 1].KiaiMode) { kiaiToggleCount++; } } } //Check for duplicate control points if (i > 0 && startOffset == (int)points[i - 1].Offset) { stackedPointCount++; for (int z = 1; z <= stackedPointCount; z++) { if (p.TimingChange == points[i - z].TimingChange) { Reports.Add(new AiReport(startOffset, Severity.Warning, LocalisationManager.GetString(OsuString.AITiming_OverlappingTimingPoints), 0, null)); break; } } } else { stackedPointCount = 0; } //snap if (p.KiaiMode && !kiaiOn) { kiaiOn = true; if (AiMod.TestBeatSnap(startOffset, false)) { Reports.Add(new AiReport(startOffset, Severity.Warning, LocalisationManager.GetString(OsuString.AITiming_UnsnappedKiai), 0, null)); } if (p.Offset - lastKiaiEndTime < 15000 && p.Offset - lastKiaiEndTime > 10) { Reports.Add(new AiReport(startOffset, Severity.Warning, LocalisationManager.GetString(OsuString.AITiming_KiaiTooShort), -1, null)); } } else if (!p.KiaiMode && kiaiOn) { kiaiOn = false; if (AiMod.TestBeatSnap(startOffset, false)) { Reports.Add(new AiReport(startOffset, Severity.Warning, LocalisationManager.GetString(OsuString.AITiming_UnsnappedKiaiEnd), 0, null)); } lastKiaiEndTime = p.Offset; } //volume if (p.Volume < 5) { lowVolumeCount++; } } //zero volume if (lowVolumeCount == points.Count) { Reports.Add(new AiReport(Severity.Error, LocalisationManager.GetString(OsuString.AITiming_AllTimingSectionsQuiet))); } else if (lowVolumeCount > 0) { Reports.Add(new AiReport(Severity.Warning, string.Format(LocalisationManager.GetString(OsuString.AITiming_SomeTimingSectionsQuiet), lowVolumeCount, points.Count))); } //KIAI if (drainLength > 3 * 60 * 1000 && kiaiLength > drainLength / 3) { Reports.Add(new AiReport(-1, Severity.Warning, LocalisationManager.GetString(OsuString.AITiming_TooMuchKiai), -1, null)); } else if (drainLength < 90 * 1000 && kiaiLength > drainLength / 2) { Reports.Add(new AiReport(-1, Severity.Warning, LocalisationManager.GetString(OsuString.AITiming_TooMuchKiaiTvSize), -1, null)); } if (BeatmapManager.Current.PreviewTime < 0) { Reports.Add(new AiReport(-1, Severity.Warning, LocalisationManager.GetString(OsuString.AITiming_NoPreviewPoint), -1, null)); } if (kiaiOn) { Reports.Add(new AiReport(-1, Severity.Warning, LocalisationManager.GetString(OsuString.AITiming_NoKiaiEnd), -1, null)); } //audio quality TAG_INFO info = AudioEngine.GetAudioTagInfo(); if (info != null) { if (info.bitrate < 128) { Reports.Add(new AiReport(Severity.Warning, LocalisationManager.GetString(OsuString.AITiming_LowAudioBitrate))); } else if (info.bitrate > 192) { Reports.Add(new AiReport(Severity.Warning, LocalisationManager.GetString(OsuString.AITiming_HighAudioBitrate))); } } }