public AiReportOneObject(HitObjectBase h, int time, BeenCorrectedDelegate corrected, Severity severity, string information, int weblink) : base(time, severity, information, weblink, corrected) { this.h1 = h; RelatedHitObjects.Add(h); }
public AiReportTwoObjects(HitObjectBase h1, HitObjectBase h2, BeenCorrectedDelegate corrected, Severity severity, string information, int weblink) : base((h1.EndTime - h2.StartTime) / 2 + h2.StartTime, severity, information, weblink, corrected) { this.h1 = h1; this.h2 = h2; RelatedHitObjects.Add(h1); RelatedHitObjects.Add(h2); }
private static float getSpacingFactor(HitObjectBase h1, HitObjectBase h2, HitObjectManagerBase hitObjectManager) { int lastSelectedTime = h1.EndTime; int nextSelectedTime = h2.StartTime; int time = nextSelectedTime - lastSelectedTime; float mul = (float)hitObjectManager.SliderVelocityAt(nextSelectedTime); return(1000.0f * (h2.Position - h1.EndPosition).Length() / (mul * time)); }
protected virtual void checkDistanceSpacing(HitObjectBase h, HitObjectBase h2, HitObjectManagerBase hitObjectManager) { //check combo distance if (!CheckDistanceSnap) { return; } if (h2 == null || h2.NewCombo || h2 is Spinner) { return; } float thisSpacingFactor = getSpacingFactor(h, h2, hitObjectManager); //If the current hitobject is a new combo, get a new distance factor from the current and next object. if (h.NewCombo || comboSpacingFactor < MIN_SPACING_FACTOR) { comboSpacingFactor = thisSpacingFactor; comboSpacingObject1 = h; comboSpacingObject2 = h2; return; } if (comboSpacingFactor > MIN_SPACING_FACTOR && thisSpacingFactor > MIN_SPACING_FACTOR && (thisSpacingFactor - comboSpacingFactor > comboSpacingFactor * 0.1 * spacingFactor || comboSpacingFactor - thisSpacingFactor > comboSpacingFactor * 0.2 * spacingFactor)) { HitObjectBase object1 = comboSpacingObject1; HitObjectBase object2 = comboSpacingObject2; AiReportTwoObjects report = new AiReportTwoObjects( h, h2, delegate { return(Math.Abs(getSpacingFactor(h, h2, hitObjectManager) - getSpacingFactor(object1, object2, hitObjectManager)) < ERROR_FACTOR); }, Severity.Warning, thisSpacingFactor < comboSpacingFactor ? LocalisationManager.GetString(OsuString.AICompose_ObjectTooClose) : LocalisationManager.GetString(OsuString.AICompose_ObjectTooFar), 82809); Reports.Add(report); } }
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); } } } }
/// <summary> /// Naive matching of cloned HitObjectBase to HitObject /// </summary> private HitObject matchObject(HitObjectBase match) { return(editor.hitObjectManager.hitObjects.Find(h => h.StartTime == match.StartTime)); }
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; } }
public AiReportDifficultyTwoObjects(BeatmapDifficulty difficulty, string condition, HitObjectBase h1, HitObjectBase h2) : base(h1, h2, null, Severity.Info, difficulty + " criteria: " + condition, 0) { }
public tpHitObject(HitObjectBase BaseHitObject, float CircleRadius) { this.BaseHitObject = BaseHitObject; // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps. float ScalingFactor = (52.0f / CircleRadius); NormalizedStartPosition = BaseHitObject.Position * ScalingFactor; // Calculate approximation of lazy movement on the slider if ((BaseHitObject.Type & HitObjectType.Slider) > 0) { float SliderFollowCircleRadius = CircleRadius * 3; // Not sure if this is correct, but here we do not need 100% exact values. This comes pretty darn close in my tests. int SegmentLength = BaseHitObject.Length / BaseHitObject.SegmentCount; int SegmentEndTime = BaseHitObject.StartTime + SegmentLength; // For simplifying this step we use actual osu! coordinates and simply scale the length, that we obtain by the ScalingFactor later Vector2 CursorPos = BaseHitObject.Position; // // Actual computation of the first lazy curve for (int Time = BaseHitObject.StartTime + LAZY_SLIDER_STEP_LENGTH; Time < SegmentEndTime; Time += LAZY_SLIDER_STEP_LENGTH) { Vector2 Difference = BaseHitObject.PositionAtTime(Time) - CursorPos; float Distance = Difference.Length(); // Did we move away too far? if (Distance > SliderFollowCircleRadius) { // Yep, we need to move the cursor Difference.Normalize(); // Obtain the direction of difference. We do no longer need the actual difference Distance -= SliderFollowCircleRadius; CursorPos += Difference * Distance; // We move the cursor just as far as needed to stay in the follow circle LazySliderLengthFirst += Distance; } } LazySliderLengthFirst *= ScalingFactor; // If we have an odd amount of repetitions the current position will be the end of the slider. Note that this will -always- be triggered if // BaseHitObject.SegmentCount <= 1, because BaseHitObject.SegmentCount can not be smaller than 1. Therefore NormalizedEndPosition will always be initialized if (BaseHitObject.SegmentCount % 2 == 1) { NormalizedEndPosition = CursorPos * ScalingFactor; } // If we have more than one segment, then we also need to compute the length ob subsequent lazy curves. They are different from the first one, since the first // one starts right at the beginning of the slider. if (BaseHitObject.SegmentCount > 1) { // Use the next segment SegmentEndTime += SegmentLength; for (int Time = SegmentEndTime - SegmentLength + LAZY_SLIDER_STEP_LENGTH; Time < SegmentEndTime; Time += LAZY_SLIDER_STEP_LENGTH) { Vector2 Difference = BaseHitObject.PositionAtTime(Time) - CursorPos; float Distance = Difference.Length(); // Did we move away too far? if (Distance > SliderFollowCircleRadius) { // Yep, we need to move the cursor Difference.Normalize(); // Obtain the direction of difference. We do no longer need the actual difference Distance -= SliderFollowCircleRadius; CursorPos += Difference * Distance; // We move the cursor just as far as needed to stay in the follow circle LazySliderLengthSubsequent += Distance; } } LazySliderLengthSubsequent *= ScalingFactor; // If we have an even amount of repetitions the current position will be the end of the slider if (BaseHitObject.SegmentCount % 2 == 1) { NormalizedEndPosition = CursorPos * ScalingFactor; } } } // We have a normal HitCircle or a spinner else { NormalizedEndPosition = BaseHitObject.EndPosition * ScalingFactor; } }
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); } }
public AiReportDifficultyOneObject(BeatmapDifficulty difficulty, string condition, HitObjectBase h1) : base(h1, h1.StartTime, null, Severity.Info, difficulty + " criteria: " + condition, 0) { }