private void UpdateStream() { int start = m_slider.StartTime; double length = m_slider.SpatialLength; double beatsnap = (double)GetBeatSnap(); double spacing; int count; if (radCount.Checked) { count = (int)(numericCount.Value); spacing = length / (double)Math.Max(1, count - 1); } else { spacing = m_slider.Velocity * AudioEngine.BeatLengthAt(start, false) * (double)(numericDistanceSnap.Value) / (beatsnap * 1000); count = (int)((length + 1.0d) / spacing) + 1; } if (spacing == lastSpacing || count == 0) { return; } double progress = 0.0d; double increment = AudioEngine.BeatLengthAt(start, false) / beatsnap; double time = start; editor.changeManager.AllowFinish = false; editor.Compose.DeleteSelection(); editor.FinishOnBreakUpdate = false; editor.changeManager.AllowFinish = true; editor.changeManager.PushAction(ChangeType.HitObject, ActionType.Add); bool replace = true; for (int x = 0; x < count; x++) { Vector2 position = m_slider.positionAtLength((float)progress); HitCircleOsu h = new HitCircleOsu(editor.hitObjectManager, position, (int)time, x == 0 && m_slider.NewCombo, false, false, false, 0); editor.hitObjectManager.Add(h, true); editor.Compose.Select(h); editor.changeManager.BackupData(h, replace); replace = false; time += increment; progress += spacing; } editor.hitObjectManager.Sort(true); editor.BreakDirty = true; }
internal static double TimeFromOffset(int offset) { HitObjectManager hom = null; if (GameBase.Mode == OsuModes.Play) { hom = Player.Instance.hitObjectManager; } else if (GameBase.Mode == OsuModes.Edit) { hom = Editor.Instance.hitObjectManager; } if (hom == null) { return(AudioEngine.BeatLengthAt(offset, BeatmapManager.Current.PlayMode == PlayModes.OsuMania)); } return(hom.Beatmap.BeatLengthAt(offset, hom.Beatmap.PlayMode == PlayModes.OsuMania)); }
/// <summary> /// Beats the snap value. /// </summary> /// <param name="time">The time which should be snapped.</param> /// <param name="snapDivisor">The snap divisor (the bottom number of a fraction).</param> /// <param name="original">Assuming we want to move *away* from an old timing (ie. jump left 1 notch) set this to the original.</param> /// <returns></returns> internal int BeatSnapValue(double time, int snapDivisor, double original) { if (AudioEngine.ControlPoints.Count == 0) { return((int)time); } double offset = AudioEngine.BeatOffsetCloseToZeroAt(original); double increment = AudioEngine.BeatLengthAt(original, false) / snapDivisor; int flooredCount; if (time - offset < 0) { flooredCount = (int)((time - offset) / increment) - 1; } else { flooredCount = (int)((time - offset) / increment); } int leftVal = (int)(flooredCount * increment + offset); int rightVal = (int)((flooredCount + 1) * increment + offset); if (original != time) { if (leftVal == original) { return(rightVal); } if (rightVal == original) { return(leftVal); } } return(time - leftVal < rightVal - time ? leftVal : rightVal); }
internal void BeatShiftCircle(int direction) { if (editor.Compose.selectedObjects.Count == 0) { return; } double increment = AudioEngine.BeatLengthAt(editor.Compose.selectedObjects[0].StartTime, false) / editor.BeatSnapDivisor; changeManager.BeginAction(ChangeType.HitObject, ActionType.MoveTimeline, Modifier.VariableChanges); changeManager.PushAction(ActionType.ChangeLength, Modifier.VariableChanges); changeManager.BackupUnusedActions(editor.Compose.selectedObjects); foreach (HitObject h in editor.Compose.selectedObjects) { h.ModifyTime(BeatSnapValue(h.StartTime) + (int)(direction * increment)); } hitObjectManager.Sort(true); editor.FinishOnBreakUpdate = true; editor.UpdateBreaks(); }
protected override void runTimingRules() { EventManager manager = Editor.Instance.eventManager; List <ControlPoint> points = AudioEngine.ControlPoints.FindAll(t => t.KiaiMode == true); for (int i = 0; i < points.Count; i++) { if (i < points.Count - 1 && (points[i + 1].Offset - points[i].Offset) < AudioEngine.BeatLengthAt(points[i].Offset, false) * 4) { Reports.Add(new AiReport(Severity.Error, LocalisationManager.GetString(OsuString.AITimingTaiko_KiaiToggledTooOften))); } } base.runTimingRules(); }
public override void Draw() { TapWindow.Draw(); List <Line> lineList = new List <Line>(); Vector2 vCentre = new Vector2(330, 360); //Determines the size of the metronome float hitObjectRadius = editor.hitObjectManager.HitObjectRadius; ControlPoint active = AudioEngine.ActiveControlPoint; float mul = active != null ? active.BpmMultiplier : 1; // Avoid nullref when no sections exist. float dist = (float)(100 * BeatmapManager.Current.DifficultySliderMultiplier / mul); // Length of one beat float count = (int)(300 / dist); // Number of beats // Fix for very fast slider speeds: make the slider half a beat long. // This fix should hold for speeds up to 6.0x if (count == 0) { count = 0.5f; } float totalLength = count * dist; Vector2 off = new Vector2(totalLength * 0.5f, 0); lineList.Add( new Line(GameBase.WindowManager.ApplyRatio(vCentre - off), GameBase.WindowManager.ApplyRatio(vCentre + off))); GameBase.LineManager.Draw(lineList, hitObjectRadius * GameBase.WindowManager.RatioInverse, backgroundColour, 0, @"Standard", true); lineList.Clear(); // fix tick spacing for slider (BPM) multiplier sections Vector2 pointDist = new Vector2((float)editor.hitObjectManager.SliderScoringPointDistance / mul, 0); Vector2 cp = vCentre - off; // Fix rounding error causing a missing tick. // In a perfect world, we could use tick RATE here, not distance. while (cp.X <= vCentre.X + off.X + 1.0f) { lineList.Add(new Line(GameBase.WindowManager.ApplyRatio(cp), GameBase.WindowManager.ApplyRatio(cp))); cp += pointDist; } cp = vCentre - off; GameBase.LineManager.Draw(lineList, hitObjectRadius * 0.2F * editor.hitObjectManager.SpriteRatio, Color.White, 0, @"Standard", true); lineList.Clear(); double first = AudioEngine.BeatOffsetAt(AudioEngine.Time); // AllowMultiplier: FALSE, otherwise we compensate for slider multipliers TWICE and get the wrong value. double f = (AudioEngine.Time - first) / AudioEngine.BeatLengthAt(AudioEngine.Time, false); f *= dist; f = f % (totalLength * 2); while (f < 0) { f += totalLength; } if (f >= totalLength) { f = (totalLength * 2) - f; } cp += new Vector2((float)f, 0); lineList.Add(new Line(GameBase.WindowManager.ApplyRatio(cp), GameBase.WindowManager.ApplyRatio(cp))); GameBase.LineManager.Draw(lineList, hitObjectRadius * GameBase.WindowManager.RatioInverse, metronomeColour, 0, @"Standard", true); }
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); } }