@rian8337/osu-difficulty-calculator 4.0.0-beta.74 → 4.0.0-beta.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +84 -44
- package/package.json +2 -2
- package/typings/index.d.ts +15 -1
package/dist/index.js
CHANGED
|
@@ -849,15 +849,18 @@ class DroidAimEvaluator {
|
|
|
849
849
|
* Calculates the flow aim strain of a hitobject.
|
|
850
850
|
*/
|
|
851
851
|
static flowAimStrainOf(current) {
|
|
852
|
-
var _a
|
|
852
|
+
var _a;
|
|
853
853
|
let speedBonus = 1;
|
|
854
854
|
if (current.strainTime < this.minSpeedBonus) {
|
|
855
855
|
speedBonus +=
|
|
856
856
|
0.75 *
|
|
857
857
|
Math.pow((this.minSpeedBonus - current.strainTime) / 40, 2);
|
|
858
858
|
}
|
|
859
|
-
const
|
|
860
|
-
|
|
859
|
+
const prev = current.previous(0);
|
|
860
|
+
// Punish low spacing as it is easier to aim.
|
|
861
|
+
const travelDistance = (_a = prev === null || prev === void 0 ? void 0 : prev.travelDistance) !== null && _a !== void 0 ? _a : 0;
|
|
862
|
+
const distance = travelDistance + current.minimumJumpDistance;
|
|
863
|
+
const shortDistancePenalty = Math.min(1, Math.pow(distance / this.singleSpacingThreshold, 3.5));
|
|
861
864
|
return (200 * speedBonus * shortDistancePenalty) / current.strainTime;
|
|
862
865
|
}
|
|
863
866
|
static calculateWideAngleBonus(angle) {
|
|
@@ -872,7 +875,7 @@ DroidAimEvaluator.acuteAngleMultiplier = 2.6;
|
|
|
872
875
|
DroidAimEvaluator.sliderMultiplier = 1.35;
|
|
873
876
|
DroidAimEvaluator.velocityChangeMultiplier = 0.75;
|
|
874
877
|
DroidAimEvaluator.wiggleMultiplier = 1.02;
|
|
875
|
-
DroidAimEvaluator.singleSpacingThreshold =
|
|
878
|
+
DroidAimEvaluator.singleSpacingThreshold = DroidDifficultyHitObject.normalizedDiameter;
|
|
876
879
|
// 200 1/4 BPM delta time
|
|
877
880
|
DroidAimEvaluator.minSpeedBonus = 75;
|
|
878
881
|
|
|
@@ -1028,7 +1031,7 @@ class DroidAim extends DroidSkill {
|
|
|
1028
1031
|
this.reducedSectionCount = 10;
|
|
1029
1032
|
this.reducedSectionBaseline = 0.75;
|
|
1030
1033
|
this.starsPerDouble = 1.05;
|
|
1031
|
-
this.skillMultiplier =
|
|
1034
|
+
this.skillMultiplier = 26.5;
|
|
1032
1035
|
this.currentAimStrain = 0;
|
|
1033
1036
|
this.sliderStrains = [];
|
|
1034
1037
|
this.withSliders = withSliders;
|
|
@@ -1230,7 +1233,7 @@ class DroidFlashlight extends DroidSkill {
|
|
|
1230
1233
|
this.reducedSectionCount = 0;
|
|
1231
1234
|
this.reducedSectionBaseline = 1;
|
|
1232
1235
|
this.starsPerDouble = 1.06;
|
|
1233
|
-
this.skillMultiplier = 0.
|
|
1236
|
+
this.skillMultiplier = 0.023;
|
|
1234
1237
|
this.currentFlashlightStrain = 0;
|
|
1235
1238
|
this.withSliders = withSliders;
|
|
1236
1239
|
}
|
|
@@ -1501,7 +1504,6 @@ class DroidTapEvaluator {
|
|
|
1501
1504
|
* - and the strain time cap.
|
|
1502
1505
|
*
|
|
1503
1506
|
* @param current The current object.
|
|
1504
|
-
* @param greatWindow The great hit window of the current object.
|
|
1505
1507
|
* @param considerCheesability Whether to consider cheesability.
|
|
1506
1508
|
* @param strainTimeCap The strain time to cap the object's strain time to.
|
|
1507
1509
|
*/
|
|
@@ -1511,7 +1513,6 @@ class DroidTapEvaluator {
|
|
|
1511
1513
|
current.isOverlapping(false)) {
|
|
1512
1514
|
return 0;
|
|
1513
1515
|
}
|
|
1514
|
-
// Nerf doubletappable doubles.
|
|
1515
1516
|
const doubletapness = considerCheesability
|
|
1516
1517
|
? 1 - current.doubletapness
|
|
1517
1518
|
: 1;
|
|
@@ -1646,7 +1647,7 @@ class DroidVisualEvaluator {
|
|
|
1646
1647
|
current.index === 0) {
|
|
1647
1648
|
return 0;
|
|
1648
1649
|
}
|
|
1649
|
-
// Start with base density and give global bonus for Hidden
|
|
1650
|
+
// Start with base density and give global bonus for Hidden.
|
|
1650
1651
|
// Add density caps for sanity.
|
|
1651
1652
|
let strain;
|
|
1652
1653
|
if (mods.has(osuBase.ModHidden)) {
|
|
@@ -1737,7 +1738,7 @@ class DroidVisual extends DroidSkill {
|
|
|
1737
1738
|
this.strainDecayBase = 0.1;
|
|
1738
1739
|
this.currentVisualStrain = 0;
|
|
1739
1740
|
this.currentRhythmMultiplier = 1;
|
|
1740
|
-
this.skillMultiplier =
|
|
1741
|
+
this.skillMultiplier = 11.2;
|
|
1741
1742
|
this.withSliders = withSliders;
|
|
1742
1743
|
}
|
|
1743
1744
|
strainValueAt(current) {
|
|
@@ -2064,6 +2065,12 @@ DroidDifficultyCalculator.threeFingerStrainThreshold = 175;
|
|
|
2064
2065
|
* The base class of performance calculators.
|
|
2065
2066
|
*/
|
|
2066
2067
|
class PerformanceCalculator {
|
|
2068
|
+
/**
|
|
2069
|
+
* Whether this score uses classic slider accuracy.
|
|
2070
|
+
*/
|
|
2071
|
+
get usingClassicSliderAccuracy() {
|
|
2072
|
+
return this._usingClassicSliderAccuracy;
|
|
2073
|
+
}
|
|
2067
2074
|
/**
|
|
2068
2075
|
* @param difficultyAttributes The difficulty attributes to calculate.
|
|
2069
2076
|
*/
|
|
@@ -2080,6 +2087,17 @@ class PerformanceCalculator {
|
|
|
2080
2087
|
* The amount of misses that are filtered out from sliderbreaks.
|
|
2081
2088
|
*/
|
|
2082
2089
|
this.effectiveMissCount = 0;
|
|
2090
|
+
/**
|
|
2091
|
+
* The amount of slider ends dropped in the score.
|
|
2092
|
+
*/
|
|
2093
|
+
this.sliderEndsDropped = 0;
|
|
2094
|
+
/**
|
|
2095
|
+
* The amount of slider ticks missed in the score.
|
|
2096
|
+
*
|
|
2097
|
+
* This is used to calculate the slider accuracy.
|
|
2098
|
+
*/
|
|
2099
|
+
this.sliderTicksMissed = 0;
|
|
2100
|
+
this._usingClassicSliderAccuracy = false;
|
|
2083
2101
|
/**
|
|
2084
2102
|
* Nerf factor used for nerfing beatmaps with very likely dropped sliderends.
|
|
2085
2103
|
*/
|
|
@@ -2160,7 +2178,20 @@ class PerformanceCalculator {
|
|
|
2160
2178
|
}
|
|
2161
2179
|
const maxCombo = this.difficultyAttributes.maxCombo;
|
|
2162
2180
|
const miss = this.computedAccuracy.nmiss;
|
|
2163
|
-
|
|
2181
|
+
let combo = (_b = options === null || options === void 0 ? void 0 : options.combo) !== null && _b !== void 0 ? _b : maxCombo - miss;
|
|
2182
|
+
if ((options === null || options === void 0 ? void 0 : options.sliderEndsDropped) !== undefined &&
|
|
2183
|
+
(options === null || options === void 0 ? void 0 : options.sliderTicksMissed) !== undefined) {
|
|
2184
|
+
this._usingClassicSliderAccuracy = false;
|
|
2185
|
+
this.sliderEndsDropped = options.sliderEndsDropped;
|
|
2186
|
+
this.sliderTicksMissed = options.sliderTicksMissed;
|
|
2187
|
+
}
|
|
2188
|
+
else {
|
|
2189
|
+
this._usingClassicSliderAccuracy = true;
|
|
2190
|
+
this.sliderEndsDropped = 0;
|
|
2191
|
+
this.sliderTicksMissed = 0;
|
|
2192
|
+
}
|
|
2193
|
+
// Ensure that combo is within possible bounds.
|
|
2194
|
+
combo = osuBase.MathUtils.clamp(combo, 0, maxCombo - miss - this.sliderEndsDropped - this.sliderTicksMissed);
|
|
2164
2195
|
this.effectiveMissCount = this.calculateEffectiveMissCount(combo, maxCombo);
|
|
2165
2196
|
if (this.mods.has(osuBase.ModNoFail)) {
|
|
2166
2197
|
this.finalMultiplier *= Math.max(0.9, 1 - 0.02 * this.effectiveMissCount);
|
|
@@ -2189,18 +2220,26 @@ class PerformanceCalculator {
|
|
|
2189
2220
|
this.computedAccuracy.n100 * n100Multiplier +
|
|
2190
2221
|
this.computedAccuracy.n50 * n50Multiplier, this.totalHits);
|
|
2191
2222
|
}
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2223
|
+
const { aimDifficultSliderCount, sliderFactor } = this.difficultyAttributes;
|
|
2224
|
+
if (aimDifficultSliderCount > 0) {
|
|
2225
|
+
let estimateImproperlyFollowedDifficultSliders;
|
|
2226
|
+
if (this.usingClassicSliderAccuracy) {
|
|
2227
|
+
// When the score is considered classic (regardless if it was made on old client or not),
|
|
2228
|
+
// we consider all missing combo to be dropped difficult sliders.
|
|
2229
|
+
estimateImproperlyFollowedDifficultSliders = osuBase.MathUtils.clamp(Math.min(this.totalImperfectHits, maxCombo - combo), 0, aimDifficultSliderCount);
|
|
2230
|
+
}
|
|
2231
|
+
else {
|
|
2232
|
+
// We add tick misses here since they too mean that the player didn't follow the slider
|
|
2233
|
+
// properly. However aren't adding misses here because missing slider heads has a harsh
|
|
2234
|
+
// penalty by itself and doesn't mean that the rest of the slider wasn't followed properly.
|
|
2235
|
+
estimateImproperlyFollowedDifficultSliders = osuBase.MathUtils.clamp(this.sliderEndsDropped + this.sliderTicksMissed, 0, aimDifficultSliderCount);
|
|
2236
|
+
}
|
|
2198
2237
|
this.sliderNerfFactor =
|
|
2199
|
-
(1 -
|
|
2238
|
+
(1 - sliderFactor) *
|
|
2200
2239
|
Math.pow(1 -
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2240
|
+
estimateImproperlyFollowedDifficultSliders /
|
|
2241
|
+
aimDifficultSliderCount, 3) +
|
|
2242
|
+
sliderFactor;
|
|
2204
2243
|
}
|
|
2205
2244
|
}
|
|
2206
2245
|
/**
|
|
@@ -2223,18 +2262,31 @@ class PerformanceCalculator {
|
|
|
2223
2262
|
* Calculates the amount of misses + sliderbreaks from combo.
|
|
2224
2263
|
*/
|
|
2225
2264
|
calculateEffectiveMissCount(combo, maxCombo) {
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
if (
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
//
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2265
|
+
let missCount = this.computedAccuracy.nmiss;
|
|
2266
|
+
const { sliderCount } = this.difficultyAttributes;
|
|
2267
|
+
if (sliderCount > 0) {
|
|
2268
|
+
if (this.usingClassicSliderAccuracy || this.mode === osuBase.Modes.droid) {
|
|
2269
|
+
// Consider that full combo is maximum combo minus dropped slider tails since
|
|
2270
|
+
// they don't contribute to combo but also don't break it.
|
|
2271
|
+
// In classic scores, we can't know the amount of dropped sliders so we estimate
|
|
2272
|
+
// to 10% of all sliders in the beatmap.
|
|
2273
|
+
const fullComboThreshold = maxCombo - 0.1 * sliderCount;
|
|
2274
|
+
if (combo < fullComboThreshold) {
|
|
2275
|
+
missCount = fullComboThreshold / Math.max(1, combo);
|
|
2276
|
+
}
|
|
2277
|
+
// In classic scores, there can't be more misses than a sum of all non-perfect judgements.
|
|
2278
|
+
missCount = Math.min(missCount, this.totalImperfectHits);
|
|
2279
|
+
}
|
|
2280
|
+
else {
|
|
2281
|
+
const fullComboThreshold = maxCombo - this.sliderEndsDropped;
|
|
2282
|
+
if (combo < fullComboThreshold) {
|
|
2283
|
+
missCount = fullComboThreshold / Math.max(1, combo);
|
|
2284
|
+
}
|
|
2285
|
+
// Combine regular misses with tick misses, since tick misses break combo as well.
|
|
2286
|
+
missCount = Math.min(missCount, this.sliderTicksMissed + this.computedAccuracy.nmiss);
|
|
2235
2287
|
}
|
|
2236
2288
|
}
|
|
2237
|
-
return
|
|
2289
|
+
return osuBase.MathUtils.clamp(missCount, this.computedAccuracy.nmiss, this.totalHits);
|
|
2238
2290
|
}
|
|
2239
2291
|
/**
|
|
2240
2292
|
* Determines whether an attribute is a cacheable attribute.
|
|
@@ -2424,7 +2476,7 @@ class DroidPerformanceCalculator extends PerformanceCalculator {
|
|
|
2424
2476
|
Math.pow(this.visual, 1.1), 1 / 1.1) * this.finalMultiplier);
|
|
2425
2477
|
}
|
|
2426
2478
|
handleOptions(options) {
|
|
2427
|
-
var _a, _b, _c, _d
|
|
2479
|
+
var _a, _b, _c, _d;
|
|
2428
2480
|
this._tapPenalty = (_a = options === null || options === void 0 ? void 0 : options.tapPenalty) !== null && _a !== void 0 ? _a : 1;
|
|
2429
2481
|
this._aimSliderCheesePenalty = (_b = options === null || options === void 0 ? void 0 : options.aimSliderCheesePenalty) !== null && _b !== void 0 ? _b : 1;
|
|
2430
2482
|
this._flashlightSliderCheesePenalty =
|
|
@@ -2432,18 +2484,6 @@ class DroidPerformanceCalculator extends PerformanceCalculator {
|
|
|
2432
2484
|
this._visualSliderCheesePenalty =
|
|
2433
2485
|
(_d = options === null || options === void 0 ? void 0 : options.visualSliderCheesePenalty) !== null && _d !== void 0 ? _d : 1;
|
|
2434
2486
|
super.handleOptions(options);
|
|
2435
|
-
const { maxCombo, aimDifficultSliderCount, sliderFactor } = this.difficultyAttributes;
|
|
2436
|
-
const combo = (_e = options === null || options === void 0 ? void 0 : options.combo) !== null && _e !== void 0 ? _e : maxCombo - this.computedAccuracy.nmiss;
|
|
2437
|
-
if (aimDifficultSliderCount > 0) {
|
|
2438
|
-
// Consider all missing combo to be dropped difficult sliders.
|
|
2439
|
-
const estimateImproperlyFollowedDifficultSliders = osuBase.MathUtils.clamp(Math.min(this.totalImperfectHits, maxCombo - combo), 0, aimDifficultSliderCount);
|
|
2440
|
-
this.sliderNerfFactor =
|
|
2441
|
-
(1 - sliderFactor) *
|
|
2442
|
-
Math.pow(1 -
|
|
2443
|
-
estimateImproperlyFollowedDifficultSliders /
|
|
2444
|
-
aimDifficultSliderCount, 3) +
|
|
2445
|
-
sliderFactor;
|
|
2446
|
-
}
|
|
2447
2487
|
}
|
|
2448
2488
|
/**
|
|
2449
2489
|
* Calculates the aim performance value of the beatmap.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rian8337/osu-difficulty-calculator",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.76",
|
|
4
4
|
"description": "A module for calculating osu!standard beatmap difficulty and performance value with respect to the current difficulty and performance algorithm.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"osu",
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "d843f2cf65e2e44c87654a2be675d6ed925193ea"
|
|
42
42
|
}
|
package/typings/index.d.ts
CHANGED
|
@@ -983,6 +983,21 @@ declare abstract class PerformanceCalculator<T extends IDifficultyAttributes> {
|
|
|
983
983
|
* The amount of misses that are filtered out from sliderbreaks.
|
|
984
984
|
*/
|
|
985
985
|
protected effectiveMissCount: number;
|
|
986
|
+
/**
|
|
987
|
+
* The amount of slider ends dropped in the score.
|
|
988
|
+
*/
|
|
989
|
+
protected sliderEndsDropped: number;
|
|
990
|
+
/**
|
|
991
|
+
* The amount of slider ticks missed in the score.
|
|
992
|
+
*
|
|
993
|
+
* This is used to calculate the slider accuracy.
|
|
994
|
+
*/
|
|
995
|
+
protected sliderTicksMissed: number;
|
|
996
|
+
private _usingClassicSliderAccuracy;
|
|
997
|
+
/**
|
|
998
|
+
* Whether this score uses classic slider accuracy.
|
|
999
|
+
*/
|
|
1000
|
+
protected get usingClassicSliderAccuracy(): boolean;
|
|
986
1001
|
/**
|
|
987
1002
|
* Nerf factor used for nerfing beatmaps with very likely dropped sliderends.
|
|
988
1003
|
*/
|
|
@@ -1298,7 +1313,6 @@ declare abstract class DroidTapEvaluator {
|
|
|
1298
1313
|
* - and the strain time cap.
|
|
1299
1314
|
*
|
|
1300
1315
|
* @param current The current object.
|
|
1301
|
-
* @param greatWindow The great hit window of the current object.
|
|
1302
1316
|
* @param considerCheesability Whether to consider cheesability.
|
|
1303
1317
|
* @param strainTimeCap The strain time to cap the object's strain time to.
|
|
1304
1318
|
*/
|