protected override double StrainValueOf(DifficultyHitObject current)
        {
            if (!(current.BaseObject is Hit))
            {
                return(0.0);
            }

            TaikoDifficultyHitObject hitObject = (TaikoDifficultyHitObject)current;

            if (hitObject.ObjectIndex % 2 == hand)
            {
                double objectStrain = 1;

                if (hitObject.ObjectIndex == 1)
                {
                    return(1);
                }

                notePairDurationHistory.Enqueue(hitObject.DeltaTime + offhandObjectDuration);

                double shortestRecentNote = notePairDurationHistory.Min();
                objectStrain += speedBonus(shortestRecentNote);

                if (hitObject.StaminaCheese)
                {
                    objectStrain *= cheesePenalty(hitObject.DeltaTime + offhandObjectDuration);
                }

                return(objectStrain);
            }

            offhandObjectDuration = hitObject.DeltaTime;
            return(0);
        }
Example #2
0
        protected override double StrainValueOf(DifficultyHitObject current)
        {
            // drum rolls and swells are exempt.
            if (!(current.BaseObject is Hit))
            {
                resetRhythmAndStrain();
                return(0.0);
            }

            currentStrain *= strain_decay;

            TaikoDifficultyHitObject hitObject = (TaikoDifficultyHitObject)current;

            notesSinceRhythmChange += 1;

            // rhythm difficulty zero (due to rhythm not changing) => no rhythm strain.
            if (hitObject.Rhythm.Difficulty == 0.0)
            {
                return(0.0);
            }

            double objectStrain = hitObject.Rhythm.Difficulty;

            objectStrain *= repetitionPenalties(hitObject);
            objectStrain *= patternLengthPenalty(notesSinceRhythmChange);
            objectStrain *= speedPenalty(hitObject.DeltaTime);

            // careful - needs to be done here since calls above read this value
            notesSinceRhythmChange = 0;

            currentStrain += objectStrain;
            return(currentStrain);
        }
Example #3
0
        protected override double StrainValueAt(DifficultyHitObject current)
        {
            currentStrain *= strainDecay(current.DeltaTime);
            currentStrain += strainValueOf(current) * skillMultiplier;

            return(currentStrain);
        }
Example #4
0
        protected override double StrainValueOf(DifficultyHitObject current)
        {
            double addition = 1;

            // We get an extra addition if we are not a slider or spinner
            if (current.LastObject is Hit && current.BaseObject is Hit && current.DeltaTime < 1000)
            {
                if (hasColourChange(current))
                {
                    addition += 0.75;
                }

                if (hasRhythmChange(current))
                {
                    addition += 1;
                }
            }
            else
            {
                lastColourSwitch = ColourSwitch.None;
                sameColourCount  = 1;
            }

            double additionFactor = 1;

            // Scale the addition factor linearly from 0.4 to 1 for DeltaTime from 0 to 50
            if (current.DeltaTime < 50)
            {
                additionFactor = 0.4 + 0.6 * current.DeltaTime / 50;
            }

            return(additionFactor * addition);
        }
        protected override double StrainValueOf(DifficultyHitObject current)
        {
            if (current.BaseObject is Spinner)
            {
                return(0);
            }

            var osuCurrent = (OsuDifficultyHitObject)current;

            double result = 0;

            if (Previous.Count > 0)
            {
                var osuPrevious = (OsuDifficultyHitObject)Previous[0];

                if (osuCurrent.Angle != null && osuCurrent.Angle.Value > angle_bonus_begin)
                {
                    const double scale = 90;

                    var angleBonus = Math.Sqrt(
                        Math.Max(osuPrevious.JumpDistance - scale, 0)
                        * Math.Pow(Math.Sin(osuCurrent.Angle.Value - angle_bonus_begin), 2)
                        * Math.Max(osuCurrent.JumpDistance - scale, 0));
                    result = 1.5 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime);
                }
            }

            double jumpDistanceExp   = applyDiminishingExp(osuCurrent.JumpDistance);
            double travelDistanceExp = applyDiminishingExp(osuCurrent.TravelDistance);

            return(Math.Max(
                       result + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(osuCurrent.StrainTime, timing_threshold),
                       (Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / osuCurrent.StrainTime
                       ));
        }
        protected override double StrainValueOf(DifficultyHitObject current)
        {
            var maniaCurrent = (ManiaDifficultyHitObject)current;
            var endTime      = maniaCurrent.BaseObject.GetEndTime();

            double holdFactor   = 1.0; // Factor in case something else is held
            double holdAddition = 0;   // Addition to the current note in case it's a hold and has to be released awkwardly

            for (int i = 0; i < columnCount; i++)
            {
                // If there is at least one other overlapping end or note, then we get an addition, buuuuuut...
                if (current.BaseObject.StartTime < holdEndTimes[i] && endTime > holdEndTimes[i])
                {
                    holdAddition = 1.0;
                }

                // ... this addition only is valid if there is _no_ other note with the same ending.
                // Releasing multiple notes at the same time is just as easy as releasing one
                if (endTime == holdEndTimes[i])
                {
                    holdAddition = 0;
                }

                // We give a slight bonus if something is held meanwhile
                if (holdEndTimes[i] > endTime)
                {
                    holdFactor = 1.25;
                }
            }

            holdEndTimes[maniaCurrent.BaseObject.Column] = endTime;

            return((1 + holdAddition) * holdFactor);
        }
        protected override double StrainValueOf(DifficultyHitObject current)
        {
            // changing from/to a drum roll or a swell does not constitute a colour change.
            // hits spaced more than a second apart are also exempt from colour strain.
            if (!(current.LastObject is Hit && current.BaseObject is Hit && current.DeltaTime < 1000))
            {
                monoHistory.Clear();

                var currentHit = current.BaseObject as Hit;
                currentMonoLength = currentHit != null ? 1 : 0;
                previousHitType   = currentHit?.Type;

                return(0.0);
            }

            var taikoCurrent = (TaikoDifficultyHitObject)current;

            double objectStrain = 0.0;

            if (previousHitType != null && taikoCurrent.HitType != previousHitType)
            {
                // The colour has changed.
                objectStrain = 1.0;

                if (monoHistory.Count < 2)
                {
                    // There needs to be at least two streaks to determine a strain.
                    objectStrain = 0.0;
                }
                else if ((monoHistory[^ 1] + currentMonoLength) % 2 == 0)
Example #8
0
        protected override double StrainValueOf(DifficultyHitObject current)
        {
            HitokoriDifficultyHitObject hitokoriCurrent = (HitokoriDifficultyHitObject)current;
            double bpm = Math.Min(hitokoriCurrent.BPM, MAX_BPM);

            return(Math.Pow(bpm / BASE_BPM, 0.6));
        }
Example #9
0
        protected override double StrainValueAt(DifficultyHitObject current)
        {
            currentStrain *= strainDecay(current.DeltaTime);
            currentStrain += FlashlightEvaluator.EvaluateDifficultyOf(current, hasHiddenMod) * skillMultiplier;

            return(currentStrain);
        }
Example #10
0
        private double strainValueOf(DifficultyHitObject current)
        {
            if (current.BaseObject is Spinner)
            {
                return(0);
            }

            var osuCurrent   = (OsuDifficultyHitObject)current;
            var osuHitObject = (OsuHitObject)(osuCurrent.BaseObject);

            double scalingFactor        = 52.0 / osuHitObject.Radius;
            double smallDistNerf        = 1.0;
            double cumulativeStrainTime = 0.0;

            double result = 0.0;

            OsuDifficultyHitObject lastObj = osuCurrent;

            // This is iterating backwards in time from the current object.
            for (int i = 0; i < Previous.Count; i++)
            {
                var currentObj       = (OsuDifficultyHitObject)Previous[i];
                var currentHitObject = (OsuHitObject)(currentObj.BaseObject);

                if (!(currentObj.BaseObject is Spinner))
                {
                    double jumpDistance = (osuHitObject.StackedPosition - currentHitObject.EndPosition).Length;

                    cumulativeStrainTime += lastObj.StrainTime;

                    // We want to nerf objects that can be easily seen within the Flashlight circle radius.
                    if (i == 0)
                    {
                        smallDistNerf = Math.Min(1.0, jumpDistance / 75.0);
                    }

                    // We also want to nerf stacks so that only the first object of the stack is accounted for.
                    double stackNerf = Math.Min(1.0, (currentObj.LazyJumpDistance / scalingFactor) / 25.0);

                    // Bonus based on how visible the object is.
                    double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - osuCurrent.OpacityAt(currentHitObject.StartTime, hidden));

                    result += stackNerf * opacityBonus * scalingFactor * jumpDistance / cumulativeStrainTime;
                }

                lastObj = currentObj;
            }

            result = Math.Pow(smallDistNerf * result, 2.0);

            // Additional bonus for Hidden due to there being no approach circles.
            if (hidden)
            {
                result *= 1.0 + hidden_bonus;
            }

            return(result);
        }
Example #11
0
File: Aim.cs Project: emu1337/osu
        protected (double snapAim, double flowAim) CalculateAimValues(DifficultyHitObject current)
        {
            var osuCurrent = (OsuDifficultyHitObject)current;

            double result         = 0;
            double controlBonus   = 0;
            double flowAngleBonus = 1.0;

            if (Previous.Count > 0)
            {
                var osuPrevious = (OsuDifficultyHitObject)Previous[0];

                if (osuCurrent.Angle != null && osuCurrent.Angle.Value > angle_bonus_begin)
                {
                    const double scale = 90;

                    var angleBonus = Math.Sqrt(
                        Math.Max(osuPrevious.JumpDistance - scale, 0)
                        * Math.Pow(Math.Sin(osuCurrent.Angle.Value - angle_bonus_begin), 2)
                        * Math.Max(osuCurrent.JumpDistance - scale, 0));
                    result = 1.475 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime);



                    double flowAngleDistanceScaling = Math.Min(Math.Max(0, osuCurrent.JumpDistance - 90) / 14.0, 1.0)
                                                      * Math.Min(Math.Max(0, osuPrevious.JumpDistance - 90) / 14.0, 1.0);
                    flowAngleBonus = Math.Sin(1.5 * (flow_angle_begin - Math.Max(Math.PI / 2, osuCurrent.Angle.Value)));
                    flowAngleBonus = 1.0 + Math.Max(0, flowAngleBonus) * flowAngleDistanceScaling * flow_angle_factor;
                }

                controlBonus = calculateControlBonus(osuCurrent, osuPrevious);
            }

            double jumpDistanceExp   = applyDiminishingExp(osuCurrent.JumpDistance);
            double travelDistanceExp = applyDiminishingExp(osuCurrent.TravelDistance);

            if (osuCurrent.OverlapScaling != null)
            {
                jumpDistanceExp   *= osuCurrent.OverlapScaling.Value;
                travelDistanceExp *= osuCurrent.OverlapScaling.Value;
            }

            double repeatJumpPenalty = calculateRepeatJumpPenalty(osuCurrent);

            double aimValue = Math.Max(
                result + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(osuCurrent.StrainTime, timing_threshold),
                (Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / osuCurrent.StrainTime
                ) + controlBonus;

            double distanceOffset = Math.Pow(jumpDistanceExp + travelDistanceExp, 1.7) / 400;
            double flowAmount     = 1.0 / (1.0 + Math.Pow(Math.E, osuCurrent.StrainTime - 126.0 + distanceOffset));
            double flowBonus      = flowAmount * flow_factor * flowAngleBonus;

            aimValue *= (1.0 - repeatJumpPenalty);
            aimValue *= (1.0 + flowBonus);

            return(aimValue * (1.0 - flowAmount), aimValue *flowAmount);
        }
        protected override double OldStrainValueOf(DifficultyHitObject current)
        {
            var catchCurrent = (CatchDifficultyHitObject)current;

            oldLastPlayerPosition ??= catchCurrent.LastNormalizedPosition;

            float playerPosition = Math.Clamp(
                oldLastPlayerPosition.Value,
                catchCurrent.NormalizedPosition - (normalized_hitobject_radius - absolute_player_positioning_error),
                catchCurrent.NormalizedPosition + (normalized_hitobject_radius - absolute_player_positioning_error)
                );

            float distanceMoved = playerPosition - oldLastPlayerPosition.Value;

            double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catchCurrent.ClockRate);

            double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510);
            double sqrtStrain       = Math.Sqrt(weightedStrainTime);

            double edgeDashBonus = 0;

            // Direction change bonus.
            if (Math.Abs(distanceMoved) > 0.1)
            {
                if (Math.Abs(oldLastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(oldLastDistanceMoved))
                {
                    double bonusFactor    = Math.Min(50, Math.Abs(distanceMoved)) / 50;
                    double antiflowFactor = Math.Max(Math.Min(70, Math.Abs(oldLastDistanceMoved)) / 70, 0.38);

                    distanceAddition += direction_change_bonus / Math.Sqrt(lastStrainTime + 16) * bonusFactor * antiflowFactor * Math.Max(1 - Math.Pow(weightedStrainTime / 1000, 3), 0);
                }

                // Base bonus for every movement, giving some weight to streams.
                distanceAddition += 12.5 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain;
            }

            // Bonus for edge dashes.
            if (catchCurrent.LastObject.DistanceToHyperDash <= 20.0f)
            {
                if (!catchCurrent.LastObject.HyperDash)
                {
                    edgeDashBonus += 5.7;
                }
                else
                {
                    // After a hyperdash we ARE in the correct position. Always!
                    playerPosition = catchCurrent.NormalizedPosition;
                }

                distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values
            }

            oldLastPlayerPosition = playerPosition;
            oldLastDistanceMoved  = distanceMoved;
            lastStrainTime        = catchCurrent.StrainTime;

            return(distanceAddition / weightedStrainTime);
        }
Example #13
0
        protected override double StrainValueOf(DifficultyHitObject current)
        {
            if (current.BaseObject is Spinner)
            {
                return(0);
            }

            var osuCurrent  = (OsuDifficultyHitObject)current;
            var osuPrevious = Previous.Count > 0 ? (OsuDifficultyHitObject)Previous[0] : null;

            double distance   = Math.Min(single_spacing_threshold, osuCurrent.TravelDistance + osuCurrent.JumpDistance);
            double strainTime = osuCurrent.StrainTime;

            double greatWindowFull  = greatWindow * 2;
            double speedWindowRatio = strainTime / greatWindowFull;

            // Aim to nerf cheesy rhythms (Very fast consecutive doubles with large deltatimes between)
            if (osuPrevious != null && strainTime < greatWindowFull && osuPrevious.StrainTime > strainTime)
            {
                strainTime = Interpolation.Lerp(osuPrevious.StrainTime, strainTime, speedWindowRatio);
            }

            // Cap deltatime to the OD 300 hitwindow.
            // 0.93 is derived from making sure 260bpm OD8 streams aren't nerfed harshly, whilst 0.92 limits the effect of the cap.
            strainTime /= Math.Clamp((strainTime / greatWindowFull) / 0.93, 0.92, 1);

            double speedBonus = 1.0;

            if (strainTime < min_speed_bonus)
            {
                speedBonus = 1 + Math.Pow((min_speed_bonus - strainTime) / speed_balancing_factor, 2);
            }

            double angleBonus = 1.0;

            if (osuCurrent.Angle != null && osuCurrent.Angle.Value < angle_bonus_begin)
            {
                angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - osuCurrent.Angle.Value)), 2) / 3.57;

                if (osuCurrent.Angle.Value < pi_over_2)
                {
                    angleBonus = 1.28;
                    if (distance < 90 && osuCurrent.Angle.Value < pi_over_4)
                    {
                        angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1);
                    }
                    else if (distance < 90)
                    {
                        angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1) * Math.Sin((pi_over_2 - osuCurrent.Angle.Value) / pi_over_4);
                    }
                }
            }

            return((1 + (speedBonus - 1) * 0.75)
                   * angleBonus
                   * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5))
                   / strainTime);
        }
Example #14
0
File: Speed.cs Project: Wieku/osu
        protected override double StrainValueAt(DifficultyHitObject current)
        {
            currentStrain *= strainDecay(((OsuDifficultyHitObject)current).StrainTime);
            currentStrain += SpeedEvaluator.EvaluateDifficultyOf(current, greatWindow) * skillMultiplier;

            currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current, greatWindow);

            return(currentStrain * currentRhythm);
        }
Example #15
0
        /// <summary>
        /// Processes the specified hit object for current strain factor.
        /// </summary>
        public void Process(DifficultyHitObject obj)
        {
            currentStrain *= GetStrainDecay(obj.DeltaTime);
            currentStrain += CalculateStrain(obj);

            currentStrainPeak = Math.Max(currentStrain, currentStrainPeak);

            previousObjects.Push(obj);
        }
Example #16
0
        /// <summary>
        /// Process a <see cref="DifficultyHitObject"/> and update current strain values accordingly.
        /// </summary>
        public void Process(DifficultyHitObject current)
        {
            currentStrain *= strainDecay(current.DeltaTime);
            currentStrain += StrainValueOf(current) * SkillMultiplier;

            currentSectionPeak = Math.Max(currentStrain, currentSectionPeak);

            Previous.Push(current);
        }
Example #17
0
File: Speed.cs Project: roridev/osu
        protected override double StrainValueAt(DifficultyHitObject current)
        {
            currentStrain *= strainDecay(current.DeltaTime);
            currentStrain += strainValueOf(current) * skillMultiplier;

            currentRhythm = calculateRhythmBonus(current);

            return(currentStrain * currentRhythm);
        }
Example #18
0
        protected override double StrainValueOf(DifficultyHitObject current)
        {
            // No need to aim HardBeat
            if (current.BaseObject is HardBeat)
            {
                return(0);
            }

            var tauCurrent = (TauDifficultyHitObject)current;

            var note     = (TauHitObject)current.BaseObject;
            var notePrev = (TauHitObject)current.LastObject;

            var noteDif = (TauDifficultyHitObject)current;

            var paddle_size = noteDif.beatmap.BeatmapInfo.BaseDifficulty.CircleSize;
            var jumpAngle   = Math.Abs(note.Angle - notePrev.Angle) * 0.5;

            var paddle_size_bonus = 0.01f * Math.Pow(paddle_size - 4, 3) + 1;

            double result = 0;
            double angleBonus;

            double speedmult = 1;

            if (Previous.Count > 0)
            {
                var tauPrevious = (TauDifficultyHitObject)Previous[0];
                var x           = tauPrevious.StrainTime;
                var y           = -Math.Log10(tauPrevious.StrainTime);
                speedmult = Math.Max(y + 3.2, 0);
                if (tauCurrent.Angle != null && tauCurrent.Angle.Value > angle_bonus_begin)
                {
                    const double min_jump = 5;

                    angleBonus = Math.Sqrt(
                        Math.Max(tauPrevious.JumpDistance - min_jump, 0)
                        * Math.Pow(Math.Sin(tauCurrent.Angle.Value - angle_bonus_begin), 2)
                        * Math.Max(tauCurrent.JumpDistance - min_jump, 0));
                    result = 30 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, tauPrevious.StrainTime);
                }
            }

            double jumpDistanceExp   = applyDiminishingExp(tauCurrent.JumpDistance);
            double travelDistanceExp = applyDiminishingExp(tauCurrent.TravelDistance);

            double angle_strain = (result + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(tauCurrent.StrainTime, timing_threshold));
            double flat_strain  = (Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / tauCurrent.StrainTime;

            // strainPeaks.Add(Math.Max(option1,option2));
            return(Math.Max(
                       angle_strain * paddle_size_bonus,
                       flat_strain * paddle_size_bonus
                       ) * speedmult);
        }
Example #19
0
        internal void ProcessInternal(DifficultyHitObject current)
        {
            while (Previous.Count > HistoryLength)
            {
                Previous.Dequeue();
            }

            Process(current);

            Previous.Enqueue(current);
        }
Example #20
0
        protected override double StrainValueOf(DifficultyHitObject current)
        {
            if (!(current.BaseObject is Hit))
            {
                return(0.0);
            }

            TaikoDifficultyHitObject hitObject = (TaikoDifficultyHitObject)current;

            return(getNextSingleKeyStamina(hitObject).StrainValueOf(hitObject));
        }
Example #21
0
        protected override double StrainValueOf(DifficultyHitObject current)
        {
            var    maniaCurrent   = (ManiaDifficultyHitObject)current;
            double endTime        = maniaCurrent.EndTime;
            int    column         = maniaCurrent.BaseObject.Column;
            double closestEndTime = Math.Abs(endTime - maniaCurrent.LastObject.StartTime); // Lowest value we can assume with the current information

            double holdFactor    = 1.0;                                                    // Factor to all additional strains in case something else is held
            double holdAddition  = 0;                                                      // Addition to the current note in case it's a hold and has to be released awkwardly
            bool   isOverlapping = false;

            // Fill up the holdEndTimes array
            for (int i = 0; i < holdEndTimes.Length; ++i)
            {
                // The current note is overlapped if a previous note or end is overlapping the current note body
                isOverlapping |= Precision.DefinitelyBigger(holdEndTimes[i], maniaCurrent.StartTime, 1) && Precision.DefinitelyBigger(endTime, holdEndTimes[i], 1);

                // We give a slight bonus to everything if something is held meanwhile
                if (Precision.DefinitelyBigger(holdEndTimes[i], endTime, 1))
                {
                    holdFactor = 1.25;
                }

                closestEndTime = Math.Min(closestEndTime, Math.Abs(endTime - holdEndTimes[i]));

                // Decay individual strains
                individualStrains[i] = applyDecay(individualStrains[i], current.DeltaTime, individual_decay_base);
            }

            holdEndTimes[column] = endTime;

            // The hold addition is given if there was an overlap, however it is only valid if there are no other note with a similar ending.
            // Releasing multiple notes is just as easy as releasing 1. Nerfs the hold addition by half if the closest release is release_threshold away.
            // holdAddition
            //     ^
            // 1.0 + - - - - - -+-----------
            //     |           /
            // 0.5 + - - - - -/   Sigmoid Curve
            //     |         /|
            // 0.0 +--------+-+---------------> Release Difference / ms
            //         release_threshold
            if (isOverlapping)
            {
                holdAddition = 1 / (1 + Math.Exp(0.5 * (release_threshold - closestEndTime)));
            }

            // Increase individual strain in own column
            individualStrains[column] += 2.0 * holdFactor;
            individualStrain           = individualStrains[column];

            overallStrain = applyDecay(overallStrain, current.DeltaTime, overall_decay_base) + (1 + holdAddition) * holdFactor;

            return(individualStrain + overallStrain - CurrentStrain);
        }
Example #22
0
        protected override double StrainValueOf(DifficultyHitObject current)
        {
            var tauCurrent = (TauDifficultyHitObject)current;

            double distance  = Math.Min(single_spacing_threshold, tauCurrent.TravelDistance + tauCurrent.JumpDistance);
            double deltaTime = Math.Max(max_speed_bonus, current.DeltaTime);

            double speedBonus = 1.0;

            if (deltaTime < min_speed_bonus)
            {
                speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2);
            }

            double NoteMultiplier = 1;
            double angleBonus     = 1.0;

            if (current.BaseObject is HardBeat)
            {
                NoteMultiplier = 1.5;

                // Increase Multiplier for alternating from beats to hardbeats and back
                if (tauCurrent.lastObject is Beat)
                {
                    NoteMultiplier *= 1.25;
                }
                if (tauCurrent.lastLastObject is HardBeat && tauCurrent.lastObject is Beat)
                {
                    NoteMultiplier *= 1.1;
                }
            }
            else if (tauCurrent.Angle != null && tauCurrent.Angle.Value < angle_bonus_begin)
            {
                angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - tauCurrent.Angle.Value)), 2) / 3.57;

                if (tauCurrent.Angle.Value < pi_over_2)
                {
                    angleBonus = 1.28;
                    if (distance < 90 && tauCurrent.Angle.Value < pi_over_4)
                    {
                        angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1);
                    }
                    else if (distance < 90)
                    {
                        angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1) * Math.Sin((pi_over_2 - tauCurrent.Angle.Value) / pi_over_4);
                    }
                }
            }

            speedBonus *= NoteMultiplier;
            return((1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / tauCurrent.StrainTime);
        }
Example #23
0
        public double StrainValueOf(DifficultyHitObject current)
        {
            if (previousHitTime == null)
            {
                previousHitTime = current.StartTime;
                return(0);
            }

            double objectStrain = 0.5;

            objectStrain   += speedBonus(current.StartTime - previousHitTime.Value);
            previousHitTime = current.StartTime;
            return(objectStrain);
        }
Example #24
0
File: Aim.cs Project: emu1337/osu
        protected override double StrainValueOf(DifficultyHitObject current)
        {
            if (current.BaseObject is Spinner)
            {
                return(0);
            }

            (double snapAim, double flowAim) = CalculateAimValues(current);

            double strainValue = snapAim + flowAim;

            AddTotalStrain(strainValue);

            return(strainValue);
        }
Example #25
0
        private double strainValueOf(DifficultyHitObject current)
        {
            if (current.BaseObject is Spinner)
            {
                return(0);
            }

            var osuCurrent   = (OsuDifficultyHitObject)current;
            var osuHitObject = (OsuHitObject)(osuCurrent.BaseObject);

            double scalingFactor        = 52.0 / osuHitObject.Radius;
            double smallDistNerf        = 1.0;
            double cumulativeStrainTime = 0.0;

            double result = 0.0;

            OsuDifficultyHitObject lastObj = osuCurrent;

            // This is iterating backwards in time from the current object.
            for (int i = 0; i < Previous.Count; i++)
            {
                var currentObj       = (OsuDifficultyHitObject)Previous[i];
                var currentHitObject = (OsuHitObject)(currentObj.BaseObject);

                if (!(currentObj.BaseObject is Spinner))
                {
                    double jumpDistance = (osuHitObject.StackedPosition - currentHitObject.EndPosition).Length;

                    cumulativeStrainTime += lastObj.StrainTime;

                    // We want to nerf objects that can be easily seen within the Flashlight circle radius.
                    if (i == 0)
                    {
                        smallDistNerf = Math.Min(1.0, jumpDistance / 75.0);
                    }

                    // We also want to nerf stacks so that only the first object of the stack is accounted for.
                    double stackNerf = Math.Min(1.0, (currentObj.LazyJumpDistance / scalingFactor) / 25.0);

                    result += stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime;
                }

                lastObj = currentObj;
            }

            return(Math.Pow(smallDistNerf * result, 2.0));
        }
Example #26
0
        /// <summary>
        /// Process a <see cref="DifficultyHitObject"/> and update current strain values accordingly.
        /// </summary>
        protected sealed override void Process(DifficultyHitObject current)
        {
            // The first object doesn't generate a strain, so we begin with an incremented section end
            if (Previous.Count == 0)
            {
                currentSectionEnd = Math.Ceiling(current.StartTime / SectionLength) * SectionLength;
            }

            while (current.StartTime > currentSectionEnd)
            {
                saveCurrentPeak();
                startNewSectionFrom(currentSectionEnd);
                currentSectionEnd += SectionLength;
            }

            currentSectionPeak = Math.Max(StrainValueAt(current), currentSectionPeak);
        }
Example #27
0
        protected override double StrainValueOf(DifficultyHitObject current)
        {
            HitokoriDifficultyHitObject hitokoriCurrent = (HitokoriDifficultyHitObject)current;

            double strain = CalculateAngleStrain(hitokoriCurrent.HitAngle);

            if (hitokoriCurrent.ChangedDirection)
            {
                strain *= DIRECTION_CHANGE_BONUS;
            }

            if (hitokoriCurrent.HoldAngle != null)
            {
                strain += CalculateAngleStrain(hitokoriCurrent.HoldAngle.Value);
            }

            return(1 + strain);
        }
Example #28
0
        public static double EvaluateDifficultyOf(DifficultyHitObject current, double greatWindow)
        {
            if (current.BaseObject is Spinner)
            {
                return(0);
            }

            // derive strainTime for calculation
            var osuCurrObj = (OsuDifficultyHitObject)current;
            var osuPrevObj = current.Index > 0 ? (OsuDifficultyHitObject)current.Previous(0) : null;
            var osuNextObj = (OsuDifficultyHitObject)current.Next(0);

            double strainTime      = osuCurrObj.StrainTime;
            double greatWindowFull = greatWindow * 2;
            double doubletapness   = 1;

            // Nerf doubletappable doubles.
            if (osuNextObj != null)
            {
                double currDeltaTime   = Math.Max(1, osuCurrObj.DeltaTime);
                double nextDeltaTime   = Math.Max(1, osuNextObj.DeltaTime);
                double deltaDifference = Math.Abs(nextDeltaTime - currDeltaTime);
                double speedRatio      = currDeltaTime / Math.Max(currDeltaTime, deltaDifference);
                double windowRatio     = Math.Pow(Math.Min(1, currDeltaTime / greatWindowFull), 2);
                doubletapness = Math.Pow(speedRatio, 1 - windowRatio);
            }

            // Cap deltatime to the OD 300 hitwindow.
            // 0.93 is derived from making sure 260bpm OD8 streams aren't nerfed harshly, whilst 0.92 limits the effect of the cap.
            strainTime /= Math.Clamp((strainTime / greatWindowFull) / 0.93, 0.92, 1);

            // derive speedBonus for calculation
            double speedBonus = 1.0;

            if (strainTime < min_speed_bonus)
            {
                speedBonus = 1 + 0.75 * Math.Pow((min_speed_bonus - strainTime) / speed_balancing_factor, 2);
            }

            double travelDistance = osuPrevObj?.TravelDistance ?? 0;
            double distance       = Math.Min(single_spacing_threshold, travelDistance + osuCurrObj.MinimumJumpDistance);

            return((speedBonus + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) * doubletapness / strainTime);
        }
Example #29
0
        protected override double StrainValueOf(DifficultyHitObject current)
        {
            var maniaCurrent = (ManiaDifficultyHitObject)current;
            var endTime      = maniaCurrent.BaseObject.GetEndTime();
            var column       = maniaCurrent.BaseObject.Column;

            double holdFactor   = 1.0; // Factor to all additional strains in case something else is held
            double holdAddition = 0;   // Addition to the current note in case it's a hold and has to be released awkwardly

            // Fill up the holdEndTimes array
            for (int i = 0; i < holdEndTimes.Length; ++i)
            {
                // If there is at least one other overlapping end or note, then we get an addition, buuuuuut...
                if (Precision.DefinitelyBigger(holdEndTimes[i], maniaCurrent.BaseObject.StartTime, 1) && Precision.DefinitelyBigger(endTime, holdEndTimes[i], 1))
                {
                    holdAddition = 1.0;
                }

                // ... this addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1
                if (Precision.AlmostEquals(endTime, holdEndTimes[i], 1))
                {
                    holdAddition = 0;
                }

                // We give a slight bonus to everything if something is held meanwhile
                if (Precision.DefinitelyBigger(holdEndTimes[i], endTime, 1))
                {
                    holdFactor = 1.25;
                }

                // Decay individual strains
                individualStrains[i] = applyDecay(individualStrains[i], current.DeltaTime, individual_decay_base);
            }

            holdEndTimes[column] = endTime;

            // Increase individual strain in own column
            individualStrains[column] += 2.0 * holdFactor;
            individualStrain           = individualStrains[column];

            overallStrain = applyDecay(overallStrain, current.DeltaTime, overall_decay_base) + (1 + holdAddition) * holdFactor;

            return(individualStrain + overallStrain - CurrentStrain);
        }
Example #30
0
        private bool hasColourChange(DifficultyHitObject current)
        {
            var taikoCurrent = (TaikoDifficultyHitObject)current;

            if (!taikoCurrent.HasTypeChange)
            {
                sameColourCount++;
                return(false);
            }

            var oldColourSwitch = lastColourSwitch;
            var newColourSwitch = sameColourCount % 2 == 0 ? ColourSwitch.Even : ColourSwitch.Odd;

            lastColourSwitch = newColourSwitch;
            sameColourCount  = 1;

            // We only want a bonus if the parity of the color switch changes
            return(oldColourSwitch != ColourSwitch.None && oldColourSwitch != newColourSwitch);
        }