protected virtual void runSanityRules() { List <HitObjectBase> hitObjects = hitObjectManager.GetHitObjects(); 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 for hitobjects which share the same startTime... if (i > 0 && !isLast) { if (hitObjects[i].StartTime != hitObjects[i + 1].StartTime && hitObjects[i].StartTime < hitObjects[i - 1].EndTime + 10) { HitObjectBase last = hitObjects[i - 1]; Reports.Add(new AiReportTwoObjects(last, h, delegate { return(Math.Abs(last.StartTime - h.StartTime) > 10); }, Severity.Error, LocalisationManager.GetString(OsuString.AICompose_ObjectsTooClose), -1)); } } //check snap if (AiMod.TestBeatSnap(h.StartTime, true)) { int index = i; AiReport report = new AiReportOneObject(h, h.StartTime, delegate { return(AiMod.TestBeatSnap(h.StartTime, true)); }, Severity.Warning, LocalisationManager.GetString(OsuString.AICompose_UnsnappedObject), 0); Reports.Add(report); } if (h.EndTime != h.StartTime) { if (AiMod.TestBeatSnap(h.EndTime, true)) //Need to account for slider velocity buffer. { int index = i; AiReport report = new AiReportOneObject(h, h.EndTime, delegate { return(AiMod.TestBeatSnap(h.EndTime, true)); }, Severity.Warning, LocalisationManager.GetString(OsuString.AICompose_UnsnappedObjectEnd), 0); Reports.Add(report); } } } }
protected override void runComposeRules() { HitObjectManager hom = hitObjectManager as HitObjectManager; List <HitObjectBase> hitObjects = hitObjectManager.GetHitObjects(); int column = (int)Math.Round(BeatmapManager.Current.DifficultyCircleSize); List <Dictionary <int, int> > columns = new List <Dictionary <int, int> >(column); int[] pressArr = new int[column]; int[] lastNoteInColumn = new int[column]; // used to check distance between notes on the same column for (int i = 0; i < column; i++) { columns.Add(new Dictionary <int, int>()); } for (int i = 0; i < hitObjects.Count; i++) { HitObjectBase h = hitObjects[i]; int col = hom.ManiaStage.ColumnAt(h.Position); //check snap if (AiMod.TestBeatSnap(h.StartTime, false)) { AiReport report = new AiReportOneObject(h, h.StartTime, delegate { return(AiMod.TestBeatSnap(h.StartTime, false)); }, Severity.Warning, LocalisationManager.GetString(OsuString.AICompose_UnsnappedObject), 0); Reports.Add(report); } if (h.IsType(HitObjectType.Hold)) { if (h.EndTime - h.StartTime < 10) { Reports.Add(new AiReportOneObject(h, h.StartTime, null, Severity.Error, LocalisationManager.GetString(OsuString.AIComposeMania_HoldNoteTooShort), 0)); } if (AiMod.TestBeatSnap(h.EndTime, false)) { AiReport report = new AiReportOneObject(h, h.EndTime, delegate { return(AiMod.TestBeatSnap(h.EndTime, false)); }, Severity.Warning, LocalisationManager.GetString(OsuString.AICompose_UnsnappedObjectEnd), 0); Reports.Add(report); } } if (h.StartTime != lastNoteInColumn[col] && lastNoteInColumn[col] != 0 && h.StartTime < lastNoteInColumn[col] + 10) { Reports.Add(new AiReportOneObject(h, h.StartTime, delegate { return(Math.Abs(lastNoteInColumn[col] - h.StartTime) > 10); }, Severity.Error, LocalisationManager.GetString(OsuString.AICompose_ObjectsTooClose), -1)); } //check for 7 notes simultaneously int existCount = 1; for (int j = 0; j < column; j++) { if (pressArr[j] >= h.StartTime) { existCount++; } } //pressArr[col] = h.EndTime; if (h.StartTime == h.EndTime) { pressArr[col] = h.StartTime; } else { pressArr[col] = h.EndTime - 2;//hack, holds' end are not counted here. } if (existCount >= 7) { AiReportOneObject report = new AiReportOneObject(h, h.StartTime, null, Severity.Error, LocalisationManager.GetString(OsuString.AIComposeMania_TooManyNotes), 0); Reports.Add(report); } //note stack on another note's startTime if (columns[col].ContainsKey(h.StartTime) || columns[col].ContainsKey(h.EndTime)) { AiReportOneObject report = new AiReportOneObject(h, h.StartTime, null, Severity.Error, LocalisationManager.GetString(OsuString.AIComposeMania_StackedObjects), 0); Reports.Add(report); continue; } bool needReport = false; foreach (KeyValuePair <int, int> pair in columns[col]) { //normal note, already checked above if (pair.Key == pair.Value) { continue; } if (h.StartTime <= pair.Key && h.EndTime >= pair.Key) { needReport = true; break; } if (h.StartTime <= pair.Value && h.EndTime >= pair.Value) { needReport = true; break; } if (h.StartTime >= pair.Key && h.EndTime <= pair.Value) { needReport = true; break; } if (h.StartTime < pair.Key && h.EndTime > pair.Value) { needReport = true; break; } } if (needReport) { AiReportOneObject report = new AiReportOneObject(h, h.StartTime, null, Severity.Error, LocalisationManager.GetString(OsuString.AIComposeMania_OverlappingObject), 0); Reports.Add(report); continue; } else { columns[col][h.StartTime] = h.EndTime; } // we don't want to mark this note as the previous note in the column until it's been processed lastNoteInColumn[col] = h.StartTime; } }
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))); } } }