public HitExplosion(OsuJudgementInfo judgement, OsuHitObject h = null) { this.judgement = judgement; AutoSizeAxes = Axes.Both; Origin = Anchor.Centre; Direction = FlowDirection.VerticalOnly; Spacing = new Vector2(0, 2); Position = (h?.EndPosition ?? Vector2.Zero) + judgement.PositionOffset; Children = new Drawable[] { line1 = new SpriteText { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Text = judgement.Score.GetDescription(), Font = @"Venera", TextSize = 16, }, line2 = new SpriteText { Text = judgement.Combo.GetDescription(), Font = @"Venera", TextSize = 11, } }; }
/// <summary> /// Creates a new <see cref="FollowPointConnection"/>. /// </summary> /// <param name="start">The <see cref="DrawableOsuHitObject"/> which <see cref="FollowPoint"/>s will exit from.</param> public FollowPointConnection([NotNull] OsuHitObject start) { Start = start; RelativeSizeAxes = Axes.Both; StartTime.BindTo(start.StartTimeBindable); }
static void applyFadeInAdjustment(OsuHitObject osuObject) { osuObject.TimeFadeIn = osuObject.TimePreempt * fade_in_duration_multiplier; foreach (var nested in osuObject.NestedHitObjects.OfType <OsuHitObject>()) { applyFadeInAdjustment(nested); } }
static void applyFadeInAdjustment(OsuHitObject osuObject) { osuObject.TimeFadeIn = osuObject.TimePreempt * FADE_IN_DURATION_MULTIPLIER; foreach (var nested in osuObject.NestedHitObjects.OfType <OsuHitObject>()) { applyFadeInAdjustment(nested); } }
/// <summary> /// Computes the fade time of follow point positioned between two hitobjects. /// </summary> /// <param name="start">The first <see cref="OsuHitObject"/>, where follow points should originate from.</param> /// <param name="end">The second <see cref="OsuHitObject"/>, which follow points should target.</param> /// <param name="fraction">The fractional distance along <paramref name="start"/> and <paramref name="end"/> at which the follow point is to be located.</param> /// <param name="fadeInTime">The fade-in time of the follow point/</param> /// <param name="fadeOutTime">The fade-out time of the follow point.</param> public static void GetFadeTimes(OsuHitObject start, OsuHitObject end, float fraction, out double fadeInTime, out double fadeOutTime) { double startTime = start.GetEndTime(); double duration = end.StartTime - startTime; fadeOutTime = startTime + fraction * duration; fadeInTime = fadeOutTime - PREEMPT; }
private void refreshPoints() { ClearInternal(false); var entry = Entry; if (entry?.End == null) { return; } OsuHitObject start = entry.Start; OsuHitObject end = entry.End; double startTime = start.GetEndTime(); Vector2 startPosition = start.StackedEndPosition; Vector2 endPosition = end.StackedPosition; Vector2 distanceVector = endPosition - startPosition; int distance = (int)distanceVector.Length; float rotation = (float)(Math.Atan2(distanceVector.Y, distanceVector.X) * (180 / Math.PI)); double finalTransformEndTime = startTime; for (int d = (int)(SPACING * 1.5); d < distance - SPACING; d += SPACING) { float fraction = (float)d / distance; Vector2 pointStartPosition = startPosition + (fraction - 0.1f) * distanceVector; Vector2 pointEndPosition = startPosition + fraction * distanceVector; GetFadeTimes(start, end, (float)d / distance, out double fadeInTime, out double fadeOutTime); FollowPoint fp; AddInternal(fp = Pool.Get()); fp.ClearTransforms(); fp.Position = pointStartPosition; fp.Rotation = rotation; fp.Alpha = 0; fp.Scale = new Vector2(1.5f * end.Scale); fp.AnimationStartTime.Value = fadeInTime; using (fp.BeginAbsoluteSequence(fadeInTime)) { fp.FadeIn(end.TimeFadeIn); fp.ScaleTo(end.Scale, end.TimeFadeIn, Easing.Out); fp.MoveTo(pointEndPosition, end.TimeFadeIn, Easing.Out); fp.Delay(fadeOutTime - fadeInTime).FadeOut(end.TimeFadeIn).Expire(); finalTransformEndTime = fp.LifetimeEnd; } } entry.LifetimeEnd = finalTransformEndTime; }
private void load(DrawableHitObject drawableObject, ISkinSource skin) { OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject; Sprite hitCircleSprite; SkinnableSpriteText hitCircleText; InternalChildren = new Drawable[] { hitCircleSprite = new Sprite { Texture = getTextureWithFallback(string.Empty), Colour = drawableObject.AccentColour.Value, Anchor = Anchor.Centre, Origin = Anchor.Centre, }, hitCircleText = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText { Font = OsuFont.Numeric.With(size: 40), UseFullGlyphHeight = false, }, confineMode: ConfineMode.NoScaling), new Sprite { Texture = getTextureWithFallback("overlay"), Anchor = Anchor.Centre, Origin = Anchor.Centre, } }; bool overlayAboveNumber = skin.GetConfig <OsuSkinConfiguration, bool>(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true; if (!overlayAboveNumber) { ChangeInternalChildDepth(hitCircleText, -float.MaxValue); } state.BindTo(drawableObject.State); state.BindValueChanged(updateState, true); accentColour.BindTo(drawableObject.AccentColour); accentColour.BindValueChanged(colour => hitCircleSprite.Colour = colour.NewValue, true); indexInCurrentCombo.BindTo(osuObject.IndexInCurrentComboBindable); indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); Texture getTextureWithFallback(string name) { Texture tex = null; if (!string.IsNullOrEmpty(priorityLookup)) { tex = skin.GetTexture($"{priorityLookup}{name}"); } return(tex ?? skin.GetTexture($"hitcircle{name}")); } }
public HitScanResult GetHitScanResult(int index) { OsuHitObject osuHitObject = this.beatmap.HitObjects[index]; OsuHitObject osuHitObject2 = (index + 1 < this.beatmap.HitObjects.Count) ? this.beatmap.HitObjects[index + 1] : null; if (!this.configManager.EnableHitScan || osuHitObject is OsuSpinner) { return(HitScanResult.CanHit); } if (this.lastHitScanIndex != index) { this.canMiss = (this.configManager.HitScanMissChance != 0 && this.random.Next(1, 101) <= this.configManager.HitScanMissChance); this.lastHitScanIndex = index; this.lastOnNotePosition = null; } Vector2 value = (osuHitObject is OsuSlider) ? (osuHitObject as OsuSlider).PositionAtTime(this.osuManager.CurrentTime) : osuHitObject.Position; Vector2 c = (osuHitObject2 != null) ? osuHitObject2.Position : Vector2.Zero; Vector2 vector = this.osuManager.WindowManager.ScreenToPlayfield(this.osuManager.Player.Ruleset.MousePosition); float num = Vector2.Distance(vector, value); float num2 = Vector2.Distance(vector, this.lastOnNotePosition ?? Vector2.Zero); if (this.osuManager.CurrentTime > osuHitObject.EndTime + this.hitWindow50) { if (this.configManager.HitScanMissAfterHitWindow50 && num <= this.hitObjectRadius + this.missRadius && !this.intersectsWithOtherHitObjects(index + 1)) { return(HitScanResult.ShouldHit); } return(HitScanResult.MoveToNextObject); } else { if (this.configManager.EnableHitScanPrediction) { if (num > this.hitObjectRadius * this.configManager.HitScanPredictionRadiusScale && num <= this.hitObjectRadius && this.lastOnNotePosition != null && osuHitObject2 != null && (MathHelper.GetAngle(this.lastOnNotePosition.Value, vector, c) <= (double)this.configManager.HitScanPredictionDirectionAngleTolerance || num2 <= (float)this.configManager.HitScanPredictionMaxDistance)) { return(HitScanResult.ShouldHit); } if (num <= this.hitObjectRadius * this.configManager.HitScanPredictionRadiusScale) { this.lastOnNotePosition = new Vector2?(vector); } else { this.lastOnNotePosition = null; } } if (num <= this.hitObjectRadius) { return(HitScanResult.CanHit); } if (this.canMiss && num <= this.hitObjectRadius + this.missRadius && !this.intersectsWithOtherHitObjects(index + 1)) { return(HitScanResult.CanHit); } return(HitScanResult.Wait); } }
public static OsuObjectPair?Nullable([CanBeNull] OsuHitObject first, [CanBeNull] OsuHitObject second, double gameplayRate) { if (first == null || second == null) { return(null); } return(new OsuObjectPair(first, second, gameplayRate)); }
public void AddFollowPoints(OsuHitObject hitObject) { addEntry(hitObject); var startTimeBindable = hitObject.StartTimeBindable.GetBoundCopy(); startTimeBindable.ValueChanged += _ => onStartTimeChanged(hitObject); startTimeMap[hitObject] = startTimeBindable; }
/// <summary> /// Calculates the movement time, effective distance and other details for the movement from objPrev to objCurr. /// </summary> /// <param name="fourthLastObject">Hit object four objects ago, relative to <paramref name="currentObject"/>.</param> /// <param name="secondLastObject">Hit object immediately preceding <paramref name="lastObject"/></param> /// <param name="lastObject">Hit object immediately preceding <paramref name="currentObject"/>.</param> /// <param name="currentObject">The hit object being currently considered.</param> /// <param name="nextObject">Hit object immediately succeeding <paramref name="currentObject"/>.</param> /// <param name="tapStrain">The tap strain of the current object.</param> TODO: does this have to be passed down? maybe store in the object? /// <param name="noteDensity">The visual note density of the current object.</param> TODO: above /// <param name="gameplayRate">The current rate of the gameplay clock.</param> /// <param name="hidden">Whether the hidden mod is active.</param> /// <returns>List of movements performed in attempt to hit the current object.</returns> public static List <OsuMovement> Extract( [CanBeNull] OsuHitObject secondLastObject, OsuHitObject lastObject, OsuHitObject currentObject, [CanBeNull] OsuHitObject nextObject, double tapStrain, double gameplayRate, bool hidden, double noteDensity, [CanBeNull] OsuHitObject fourthLastObject = null) { var movement = new OsuMovement(); var parameters = new MovementExtractionParameters(fourthLastObject, secondLastObject, lastObject, currentObject, nextObject, gameplayRate); movement.RawMovementTime = parameters.LastToCurrent.TimeDelta; movement.StartTime = currentObject.StartTime / 1000.0; if (currentObject is Spinner || lastObject is Spinner) { movement.Throughput = 0; movement.Distance = 0; movement.MovementTime = 1; movement.Cheesablility = 0; movement.CheeseWindow = 0; return(new List <OsuMovement> { movement }); } movement.EndsOnSlider = currentObject is Slider; double movementThroughput = FittsLaw.Throughput(parameters.LastToCurrent.RelativeLength, parameters.LastToCurrent.TimeDelta); movement.Throughput = movementThroughput; movement.Distance = correctMovementDistance(parameters, movementThroughput, tapStrain, hidden, noteDensity); calculateCheeseWindow(parameters, movementThroughput); movement.MovementTime = parameters.LastToCurrent.TimeDelta; movement.Cheesablility = parameters.Cheesability; movement.CheeseWindow = parameters.CheeseWindow; var movementWithNested = new List <OsuMovement> { movement }; // add zero difficulty movements corresponding to slider ticks/slider ends so combo is reflected properly int extraNestedCount = currentObject.NestedHitObjects.Count - 1; for (int i = 0; i < extraNestedCount; i++) { movementWithNested.Add(OsuMovement.Empty(movement.StartTime)); } return(movementWithNested); }
/// <summary> /// Initializes the object calculating extra data required for difficulty calculation. /// </summary> public OsuDifficultyHitObject(OsuHitObject currentObject, OsuHitObject lastObject, double timeRate) { this.lastObject = lastObject; this.timeRate = timeRate; BaseObject = currentObject; setDistances(); setTimingValues(); // Calculate angle here }
public OsuDifficultyHitObject(HitObject hitObject, HitObject lastLastObject, HitObject lastObject, double clockRate) : base(hitObject, lastObject, clockRate) { this.lastLastObject = (OsuHitObject)lastLastObject; this.lastObject = (OsuHitObject)lastObject; // Capped to 25ms to prevent difficulty calculation breaking from simultaneous objects. StrainTime = Math.Max(DeltaTime, min_delta_time); setDistances(clockRate); }
public OsuDifficultyHitObject(HitObject hitObject, HitObject lastLastObject, HitObject lastObject, double clockRate) : base(hitObject, lastObject, clockRate) { this.lastLastObject = (OsuHitObject)lastLastObject; this.lastObject = (OsuHitObject)lastObject; setDistances(); // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure StrainTime = Math.Max(50, DeltaTime); }
public OsuObjectPair([NotNull] OsuHitObject first, [NotNull] OsuHitObject second, double gameplayRate) { var firstPosition = Vector <double> .Build.Dense(new double[] { first.StackedPosition.X, first.StackedPosition.Y }); var secondPosition = Vector <double> .Build.Dense(new double[] { second.StackedPosition.X, second.StackedPosition.Y }); RelativeVector = (secondPosition - firstPosition) / (2 * second.Radius); RelativeLength = RelativeVector.L2Norm(); TimeDelta = (second.StartTime - first.StartTime) / gameplayRate / 1000.0; }
private Vector2 getEndCursorPosition(OsuHitObject hitObject) { Vector2 pos = hitObject.StackedPosition; if (hitObject is Slider slider) { computeSliderCursorPosition(slider); pos = slider.LazyEndPosition ?? pos; } return(pos); }
private void refreshPoints() { ClearInternal(false); OsuHitObject start = Entry.Start; OsuHitObject end = Entry.End; double startTime = start.GetEndTime(); Vector2 startPosition = start.StackedEndPosition; Vector2 endPosition = end.StackedPosition; Vector2 distanceVector = endPosition - startPosition; int distance = (int)distanceVector.Length; float rotation = (float)(Math.Atan2(distanceVector.Y, distanceVector.X) * (180 / Math.PI)); double finalTransformEndTime = startTime; for (int d = (int)(SPACING * 1.5); d < distance - SPACING; d += SPACING) { float fraction = (float)d / distance; Vector2 pointStartPosition = startPosition + (fraction - 0.1f) * distanceVector; Vector2 pointEndPosition = startPosition + fraction * distanceVector; GetFadeTimes(start, end, (float)d / distance, out var fadeInTime, out var fadeOutTime); FollowPoint fp; AddInternal(fp = Pool.Get()); fp.ClearTransforms(); fp.Position = pointStartPosition; fp.Rotation = rotation; fp.Alpha = 0; fp.Scale = new Vector2(1.5f * end.Scale); fp.AnimationStartTime.Value = fadeInTime; using (fp.BeginAbsoluteSequence(fadeInTime)) { fp.FadeIn(end.TimeFadeIn); fp.ScaleTo(end.Scale, end.TimeFadeIn, Easing.Out); fp.MoveTo(pointEndPosition, end.TimeFadeIn, Easing.Out); fp.Delay(fadeOutTime - fadeInTime).FadeOut(end.TimeFadeIn); finalTransformEndTime = fadeOutTime + end.TimeFadeIn; } } // todo: use Expire() on FollowPoints and take lifetime from them when https://github.com/ppy/osu-framework/issues/3300 is fixed. Entry.LifetimeEnd = finalTransformEndTime; }
/// <summary> /// Initializes the object calculating extra data required for difficulty calculation. /// </summary> public OsuDifficultyHitObject(OsuHitObject currentObject, OsuHitObject lastObject, OsuHitObject lastLastObject, double timeRate) { this.lastObject = lastObject; this.timeRate = timeRate; BaseObject = currentObject; setDistances(); setTimingValues(); // Calculate angle here OsuHitObject[] triangle = new OsuHitObject[] { currentObject, lastObject, lastLastObject }; calculateAngle(triangle); }
public static void GetFadeTimes(OsuHitObject start, OsuHitObject end, float fraction, out double fadeInTime, out double fadeOutTime) { double startTime = start.GetEndTime(); double duration = end.StartTime - startTime; // Preempt time can go below 800ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR. // This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear preempt function (see: OsuHitObject). // Note that this doesn't exactly match the AR>10 visuals as they're classically known, but it feels good. double preempt = PREEMPT * Math.Min(1, start.TimePreempt / OsuHitObject.PREEMPT_MIN); fadeOutTime = startTime + fraction * duration; fadeInTime = fadeOutTime - preempt; }
private void addHitObjectReplay(OsuHitObject h) { // Default values for circles/sliders Vector2 startPosition = h.StackedPosition; Easing easing = preferredEasing; float spinnerDirection = -1; // The startPosition for the slider should not be its .Position, but the point on the circle whose tangent crosses the current cursor position // We also modify spinnerDirection so it spins in the direction it enters the spin circle, to make a smooth transition. // TODO: Shouldn't the spinner always spin in the same direction? if (h is Spinner) { calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[^ 1]).Position, out startPosition, out spinnerDirection);
public DrawableHitCircle(OsuHitObject h) : base(h) { Origin = Anchor.Centre; osuObject = h; Position = osuObject.StackedPosition; Scale = new Vector2(osuObject.Scale); Children = new Drawable[] { glow = new GlowPiece { Colour = osuObject.Colour }, circle = new CirclePiece { Colour = osuObject.Colour, Hit = () => { if (Judgement.Result.HasValue) { return(false); } ((PositionalJudgementInfo)Judgement).PositionOffset = Vector2.Zero; //todo: set to correct value UpdateJudgement(true); return(true); }, }, number = new NumberPiece { Text = h is Spinner ? "S" : (HitObject.ComboIndex + 1).ToString(), }, ring = new RingPiece(), flash = new FlashPiece(), explode = new ExplodePiece { Colour = osuObject.Colour, }, ApproachCircle = new ApproachCircle { Colour = osuObject.Colour, } }; //may not be so correct Size = circle.DrawSize; }
public void GenerateCursorFollow(double startTime, double endTime) { var sprite = layer.CreateSprite("sb/spotLight.png"); sprite.ScaleVec(startTime, 1f, 4f); OsuHitObject prev = null; foreach (var hObj in Beatmap.HitObjects) { if (hObj.StartTime < startTime || hObj.StartTime > endTime) { continue; } if (prev != null) { sprite.Move(OsbEasing.InSine, prev.EndTime, hObj.StartTime, prev.EndPosition, hObj.Position); if (hObj is OsuSlider) { sprite.Move(hObj.StartTime, hObj.EndTime, hObj.Position, hObj.EndPosition); } if (prev.ColorIndex != hObj.ColorIndex) { sprite.Color(hObj.StartTime, hObj.Color); } } prev = hObj; } for (var time = startTime; time < endTime; time += beatduration) { if (time > 313864 && time < 314922) //This is a silent part in the second kiai. No movement wanted { continue; } var rotation = Random(0.3f, 0.8f) * Random(0, 2) == 0 ? 1 : -1; sprite.Fade(OsbEasing.InCubic, time, time + beatduration, .8f, 0f); sprite.Rotate(OsbEasing.OutElasticQuarter, time, time + beatduration, sprite.RotationAt(time), sprite.RotationAt(time) + rotation); } sprite.Additive(sprite.CommandsStartTime, sprite.CommandsEndTime); }
public DrawableHitCircle(OsuHitObject h) : base(h) { Origin = Anchor.Centre; Position = HitObject.StackedPosition; Scale = new Vector2(HitObject.Scale); Children = new Drawable[] { glow = new GlowPiece { Colour = AccentColour }, circle = new CirclePiece { Colour = AccentColour, Hit = () => { if (AllJudged) { return(false); } UpdateJudgement(true); return(true); }, }, number = new NumberPiece { Text = h is Spinner ? "S" : (HitObject.ComboIndex + 1).ToString(), }, ring = new RingPiece(), flash = new FlashPiece(), explode = new ExplodePiece { Colour = AccentColour, }, ApproachCircle = new ApproachCircle { Alpha = 0, Scale = new Vector2(4), Colour = AccentColour, } }; //may not be so correct Size = circle.DrawSize; }
private void addEntry(OsuHitObject hitObject) { var newEntry = new FollowPointLifetimeEntry(hitObject); int index = lifetimeEntries.AddInPlace(newEntry, Comparer <FollowPointLifetimeEntry> .Create((e1, e2) => { int comp = e1.Start.StartTime.CompareTo(e2.Start.StartTime); if (comp != 0) { return(comp); } // we always want to insert the new item after equal ones. // this is important for beatmaps with multiple hitobjects at the same point in time. // if we use standard comparison insert order, there will be a churn of connections getting re-updated to // the next object at the point-in-time, adding a construction/disposal overhead (see FollowPointConnection.End implementation's ClearInternal). // this is easily visible on https://osu.ppy.sh/beatmapsets/150945#osu/372245 return(-1); })); if (index < lifetimeEntries.Count - 1) { // Update the connection's end point to the next connection's start point // h1 -> -> -> h2 // connection nextGroup FollowPointLifetimeEntry nextEntry = lifetimeEntries[index + 1]; newEntry.End = nextEntry.Start; } else { // The end point may be non-null during re-ordering newEntry.End = null; } if (index > 0) { // Update the previous connection's end point to the current connection's start point // h1 -> -> -> h2 // prevGroup connection FollowPointLifetimeEntry previousEntry = lifetimeEntries[index - 1]; previousEntry.End = newEntry.Start; } Add(newEntry); }
/// <summary> /// Extracts movement (only for the first object in a beatmap). /// </summary> public static List <OsuMovement> ExtractMovement(OsuHitObject obj) { var movement = GetEmptyMovement(obj.StartTime / 1000.0); var movementWithNested = new List <OsuMovement> { movement }; // add zero difficulty movements corresponding to slider ticks/slider ends so combo is reflected properly int extraNestedCount = obj.NestedHitObjects.Count - 1; for (int i = 0; i < extraNestedCount; i++) { movementWithNested.Add(GetEmptyMovement(movement.Time)); } return(movementWithNested); }
private void load(DrawableHitObject drawableObject) { OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject; state.BindTo(drawableObject.State); state.BindValueChanged(updateState, true); accentColour.BindTo(drawableObject.AccentColour); accentColour.BindValueChanged(colour => { explode.Colour = colour.NewValue; glow.Colour = colour.NewValue; circle.Colour = colour.NewValue; }, true); indexInCurrentCombo.BindTo(osuObject.IndexInCurrentComboBindable); indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true); }
private void applyStackingOld(Beatmap <OsuHitObject> beatmap) { for (int i = 0; i < beatmap.HitObjects.Count; i++) { OsuHitObject currHitObject = beatmap.HitObjects[i]; if (currHitObject.StackHeight != 0 && !(currHitObject is Slider)) { continue; } double startTime = currHitObject.GetEndTime(); int sliderStack = 0; for (int j = i + 1; j < beatmap.HitObjects.Count; j++) { double stackThreshold = beatmap.HitObjects[i].TimePreempt * beatmap.BeatmapInfo.StackLeniency; if (beatmap.HitObjects[j].StartTime - stackThreshold > startTime) { break; } // The start position of the hitobject, or the position at the end of the path if the hitobject is a slider Vector2 position2 = currHitObject is Slider currSlider ? currSlider.Position + currSlider.Path.PositionAt(1) : currHitObject.Position; if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.Position) < stack_distance) { currHitObject.StackHeight++; startTime = beatmap.HitObjects[j].GetEndTime(); } else if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, position2) < stack_distance) { //Case for sliders - bump notes down and right, rather than up and left. sliderStack++; beatmap.HitObjects[j].StackHeight -= sliderStack; startTime = beatmap.HitObjects[j].GetEndTime(); } } } }
private void moveToHitObject(OsuHitObject h, Vector2 targetPos, Easing easing) { OsuReplayFrame lastFrame = (OsuReplayFrame)Frames[Frames.Count - 1]; // Wait until Auto could "see and react" to the next note. double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - reactionTime); if (waitTime > lastFrame.Time) { lastFrame = new OsuReplayFrame(waitTime, lastFrame.Position) { Actions = lastFrame.Actions }; AddFrameToReplay(lastFrame); } Vector2 lastPosition = lastFrame.Position; double timeDifference = ApplyModsToTime(h.StartTime - lastFrame.Time); // Only "snap" to hitcircles if they are far enough apart. As the time between hitcircles gets shorter the snapping threshold goes up. if (timeDifference > 0 && // Sanity checks ((lastPosition - targetPos).Length > h.Radius * (1.5 + 100.0 / timeDifference) || // Either the distance is big enough timeDifference >= 266)) // ... or the beats are slow enough to tap anyway. { // Perform eased movement for (double time = lastFrame.Time + FrameDelay; time < h.StartTime; time += FrameDelay) { Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPos, lastFrame.Time, h.StartTime, easing); AddFrameToReplay(new OsuReplayFrame((int)time, new Vector2(currentPosition.X, currentPosition.Y)) { Actions = lastFrame.Actions }); } buttonIndex = 0; } else { buttonIndex++; } }
/// <summary> /// 将在符合此物件时间范围内的鼠标动作全都提供给物件使用判断 /// </summary> /// <param name="hitObject">要判断的物件</param> /// <param name="candidate_actions">没被其他物件处理过的符合物件击打时间内的鼠标动作</param> /// <returns>鼠标动作,其内元素可能已经标记物件</returns> /// public static void Judge(OsuHitObject circle, JudgementParam param) { switch (circle) { case HitCircle c: Judge(c, param); break; case Slider c: Judge(c, param); break; case Spinner c: Judge(c, param); break; default: return; } }
private bool intersectsWithOtherHitObjects(int startIndex) { int currentTime = this.osuManager.CurrentTime; double num = this.osuManager.DifficultyRange((double)this.beatmap.ApproachRate, 1800.0, 1200.0, 450.0); Vector2 value = this.osuManager.WindowManager.ScreenToPlayfield(this.osuManager.Player.Ruleset.MousePosition); for (int i = startIndex; i < this.beatmap.HitObjects.Count; i++) { OsuHitObject osuHitObject = this.beatmap.HitObjects[i]; if ((double)osuHitObject.StartTime - num > (double)currentTime) { break; } if (Vector2.Distance(value, osuHitObject.Position) <= this.hitObjectRadius) { return(true); } } return(false); }
private void removeEntry(OsuHitObject hitObject) { int index = lifetimeEntries.FindIndex(e => e.Start == hitObject); var entry = lifetimeEntries[index]; entry.UnbindEvents(); lifetimeEntries.RemoveAt(index); Remove(entry); if (index > 0) { // Update the previous connection's end point to the next connection's start point // h1 -> -> -> h2 -> -> -> h3 // prevGroup connection nextGroup // The current connection's end point is used since there may not be a next connection FollowPointLifetimeEntry previousEntry = lifetimeEntries[index - 1]; previousEntry.End = entry.End; } }
public DrawableOsuHitObject(OsuHitObject hitObject) : base(hitObject) { }