Scale difficulty with doubletapness, make kicksliders not reduce the difficulty of the next object, adjust balancing

This commit is contained in:
StanR 2024-08-24 04:37:58 +05:00
parent f1adc6f98c
commit ce8286d299
3 changed files with 38 additions and 22 deletions

View File

@ -60,7 +60,7 @@ public override bool Equals(object? obj)
}
private const int history_time_max = 5 * 1000; // 5 seconds of calculatingRhythmBonus max.
private const double rhythm_multiplier = 1.05;
private const double rhythm_multiplier = 0.95;
/// <summary>
/// Calculates a rhythm multiplier for the difficulty of the tap associated with historic data of the current <see cref="OsuDifficultyHitObject"/>.
@ -102,7 +102,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current)
double prevDelta = prevObj.StrainTime;
double lastDelta = lastObj.StrainTime;
double currRatio = 1.0 + 8.8 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
double currRatio = 1.0 + 9.8 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses.
double windowPenalty = Math.Min(1, Math.Max(0, Math.Abs(prevDelta - currDelta) - currObj.HitWindowGreat * 0.3) / (currObj.HitWindowGreat * 0.3));
@ -121,16 +121,22 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current)
}
else
{
if (currObj.BaseObject is Slider) // bpm change is into slider, this is easy acc window
// bpm change is into slider, this is easy acc window
if (currObj.BaseObject is Slider)
effectiveRatio *= 0.125;
if (prevObj.BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle
effectiveRatio *= 0.25;
// bpm change was from a slider, this is easier typically than circle -> circle
// unintentional side effect is that bursts with kicksliders at the ends might have lower difficulty than bursts without sliders
// therefore we're checking for quick sliders and don't lower the difficulty for them since they don't really make tapping easier (no time to adjust)
if (prevObj.BaseObject is Slider && prevObj.TravelTime > prevDelta * 1.5)
effectiveRatio *= 0.15;
if (island.IsSimilarPolarity(previousIsland, deltaDifferenceEpsilon)) // repeated island polartiy (2 -> 4, 3 -> 5)
effectiveRatio *= 0.35;
// repeated island polartiy (2 -> 4, 3 -> 5)
if (island.IsSimilarPolarity(previousIsland, deltaDifferenceEpsilon))
effectiveRatio *= 0.3;
if (lastDelta > prevDelta + deltaDifferenceEpsilon && prevDelta > currDelta + deltaDifferenceEpsilon) // previous increase happened a note ago, 1/1->1/2-1/4, dont want to buff this.
// previous increase happened a note ago, 1/1->1/2-1/4, dont want to buff this.
if (lastDelta > prevDelta + deltaDifferenceEpsilon && prevDelta > currDelta + deltaDifferenceEpsilon)
effectiveRatio *= 0.125;
if (!islandCounts.TryAdd(island, 1))
@ -142,6 +148,10 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current)
effectiveRatio *= Math.Min(2.0 / islandCounts[island], Math.Pow(1.0 / islandCounts[island], power));
}
// scale down the difficulty if the object is doubletappable
double doubletapness = prevObj.GetDoubletapness((OsuDifficultyHitObject?)prevObj.Next(0));
effectiveRatio *= 1 - doubletapness * 0.75;
rhythmComplexitySum += Math.Sqrt(effectiveRatio * startRatio) * currHistoricalDecay;
startRatio = effectiveRatio;
@ -167,7 +177,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current)
prevObj = currObj;
}
return Math.Sqrt(4 + rhythmComplexitySum * rhythm_multiplier) / 2; //produces multiplier that can be applied to strain. range [1, infinity) (not really though)
return Math.Sqrt(4 + rhythmComplexitySum * rhythm_multiplier) / 2.0; //produces multiplier that can be applied to strain. range [1, infinity) (not really though)
}
private static double logistic(double x, double maxValue, double multiplier, double offset) => (maxValue / (1 + Math.Pow(Math.E, offset - (multiplier * x))));

View File

@ -30,21 +30,9 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current)
// 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 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 / osuCurrObj.HitWindowGreat), 2);
doubletapness = Math.Pow(speedRatio, 1 - windowRatio);
}
double doubletapness = 1.0 - osuCurrObj.GetDoubletapness((OsuDifficultyHitObject?)osuCurrObj.Next(0));
// 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.

View File

@ -137,6 +137,24 @@ public double OpacityAt(double time, bool hidden)
return Math.Clamp((time - fadeInStartTime) / fadeInDuration, 0.0, 1.0);
}
/// <summary>
/// Returns how possible is it to doubletap this object together with the next one and get perfect judgement in range from 0 to 1
/// </summary>
public double GetDoubletapness(OsuDifficultyHitObject? osuNextObj)
{
if (osuNextObj != null)
{
double currDeltaTime = Math.Max(1, 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 / HitWindowGreat), 2);
return 1.0 - Math.Pow(speedRatio, 1 - windowRatio);
}
return 0;
}
private void setDistances(double clockRate)
{
if (BaseObject is Slider currentSlider)