Example #1
0
        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,
                }
            };
        }
Example #2
0
        /// <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);
        }
Example #3
0
 static void applyFadeInAdjustment(OsuHitObject osuObject)
 {
     osuObject.TimeFadeIn = osuObject.TimePreempt * fade_in_duration_multiplier;
     foreach (var nested in osuObject.NestedHitObjects.OfType <OsuHitObject>())
     {
         applyFadeInAdjustment(nested);
     }
 }
Example #4
0
 static void applyFadeInAdjustment(OsuHitObject osuObject)
 {
     osuObject.TimeFadeIn = osuObject.TimePreempt * FADE_IN_DURATION_MULTIPLIER;
     foreach (var nested in osuObject.NestedHitObjects.OfType <OsuHitObject>())
     {
         applyFadeInAdjustment(nested);
     }
 }
Example #5
0
        /// <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;
        }
Example #6
0
        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;
        }
Example #7
0
        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}"));
            }
        }
Example #8
0
        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);
            }
        }
Example #9
0
        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));
        }
Example #10
0
        public void AddFollowPoints(OsuHitObject hitObject)
        {
            addEntry(hitObject);

            var startTimeBindable = hitObject.StartTimeBindable.GetBoundCopy();

            startTimeBindable.ValueChanged += _ => onStartTimeChanged(hitObject);
            startTimeMap[hitObject]         = startTimeBindable;
        }
Example #11
0
        /// <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);
        }
Example #12
0
        /// <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
        }
Example #13
0
        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);
        }
Example #14
0
        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);
        }
Example #15
0
        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;
        }
Example #16
0
        private Vector2 getEndCursorPosition(OsuHitObject hitObject)
        {
            Vector2 pos = hitObject.StackedPosition;

            if (hitObject is Slider slider)
            {
                computeSliderCursorPosition(slider);
                pos = slider.LazyEndPosition ?? pos;
            }

            return(pos);
        }
Example #17
0
        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;
        }
Example #18
0
        /// <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);
        }
Example #19
0
        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;
        }
Example #20
0
        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);
Example #21
0
        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;
        }
Example #22
0
        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);
        }
Example #23
0
        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;
        }
Example #24
0
        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);
        }
Example #25
0
        /// <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);
        }
Example #26
0
        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);
        }
Example #27
0
        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();
                    }
                }
            }
        }
Example #28
0
        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++;
            }
        }
Example #29
0
        /// <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;
            }
        }
Example #30
0
        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);
        }
Example #31
0
        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;
            }
        }
Example #32
0
 public DrawableOsuHitObject(OsuHitObject hitObject)
     : base(hitObject)
 {
 }