@rian8337/osu-difficulty-calculator 4.0.0-beta.44 → 4.0.0-beta.48

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 CHANGED
@@ -1158,8 +1158,9 @@ class DroidRhythmEvaluator {
1158
1158
  * with historic data of the current object.
1159
1159
  *
1160
1160
  * @param current The current object.
1161
+ * @param useSliderAccuracy Whether to use slider accuracy.
1161
1162
  */
1162
- static evaluateDifficultyOf(current) {
1163
+ static evaluateDifficultyOf(current, useSliderAccuracy) {
1163
1164
  if (current.object instanceof osuBase.Spinner) {
1164
1165
  return 1;
1165
1166
  }
@@ -1221,15 +1222,17 @@ class DroidRhythmEvaluator {
1221
1222
  island.addDelta(currentDelta);
1222
1223
  }
1223
1224
  else {
1224
- // BPM change is into slider, this is easy acc window.
1225
- if (currentObject.object instanceof osuBase.Slider) {
1226
- effectiveRatio /= 8;
1227
- }
1228
- // BPM change was from a slider, this is easier typically than circle -> circle.
1229
- // Unintentional side effect is that bursts with kicksliders at the ends might have lower difficulty
1230
- // than bursts without sliders.
1231
- if (prevObject.object instanceof osuBase.Slider) {
1232
- effectiveRatio *= 0.3;
1225
+ if (!useSliderAccuracy) {
1226
+ // BPM change is into slider, this is easy acc window.
1227
+ if (currentObject.object instanceof osuBase.Slider) {
1228
+ effectiveRatio /= 8;
1229
+ }
1230
+ // BPM change was from a slider, this is easier typically than circle -> circle.
1231
+ // Unintentional side effect is that bursts with kicksliders at the ends might have lower difficulty
1232
+ // than bursts without sliders.
1233
+ if (prevObject.object instanceof osuBase.Slider) {
1234
+ effectiveRatio *= 0.3;
1235
+ }
1233
1236
  }
1234
1237
  // Repeated island polarity (2 -> 4, 3 -> 5).
1235
1238
  if (island.isSimilarPolarity(previousIsland)) {
@@ -1312,18 +1315,19 @@ DroidRhythmEvaluator.rhythmRatioMultiplier = 12;
1312
1315
  * Represents the skill required to properly follow a beatmap's rhythm.
1313
1316
  */
1314
1317
  class DroidRhythm extends DroidSkill {
1315
- constructor() {
1316
- super(...arguments);
1318
+ constructor(mods) {
1319
+ super(mods);
1317
1320
  this.reducedSectionCount = 5;
1318
1321
  this.reducedSectionBaseline = 0.75;
1319
1322
  this.strainDecayBase = 0.3;
1320
1323
  this.starsPerDouble = 1.75;
1321
1324
  this.currentRhythmStrain = 0;
1322
1325
  this.currentRhythmMultiplier = 1;
1326
+ this.useSliderAccuracy = mods.some((m) => m instanceof osuBase.ModScoreV2);
1323
1327
  }
1324
1328
  strainValueAt(current) {
1325
1329
  this.currentRhythmMultiplier =
1326
- DroidRhythmEvaluator.evaluateDifficultyOf(current);
1330
+ DroidRhythmEvaluator.evaluateDifficultyOf(current, this.useSliderAccuracy);
1327
1331
  this.currentRhythmStrain *= this.strainDecay(current.deltaTime);
1328
1332
  this.currentRhythmStrain += this.currentRhythmMultiplier - 1;
1329
1333
  return this.currentRhythmStrain;
@@ -2103,12 +2107,10 @@ class PerformanceCalculator {
2103
2107
  * Nerf factor used for nerfing beatmaps with very likely dropped sliderends.
2104
2108
  */
2105
2109
  this.sliderNerfFactor = 1;
2106
- if (this.isCacheableAttribute(difficultyAttributes)) {
2107
- this.difficultyAttributes = Object.assign(Object.assign({}, difficultyAttributes), { mods: osuBase.ModUtil.pcStringToMods(difficultyAttributes.mods) });
2108
- }
2109
- else {
2110
- this.difficultyAttributes = osuBase.Utils.deepCopy(difficultyAttributes);
2111
- }
2110
+ this.difficultyAttributes = difficultyAttributes;
2111
+ this.mods = this.isCacheableAttribute(difficultyAttributes)
2112
+ ? osuBase.ModUtil.pcStringToMods(difficultyAttributes.mods)
2113
+ : difficultyAttributes.mods;
2112
2114
  }
2113
2115
  /**
2114
2116
  * Calculates the performance points of the beatmap.
@@ -2175,15 +2177,15 @@ class PerformanceCalculator {
2175
2177
  });
2176
2178
  }
2177
2179
  this.effectiveMissCount = this.calculateEffectiveMissCount(combo, maxCombo);
2178
- if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModNoFail)) {
2180
+ if (this.mods.some((m) => m instanceof osuBase.ModNoFail)) {
2179
2181
  this.finalMultiplier *= Math.max(0.9, 1 - 0.02 * this.effectiveMissCount);
2180
2182
  }
2181
- if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModSpunOut)) {
2183
+ if (this.mods.some((m) => m instanceof osuBase.ModSpunOut)) {
2182
2184
  this.finalMultiplier *=
2183
2185
  1 -
2184
2186
  Math.pow(this.difficultyAttributes.spinnerCount / this.totalHits, 0.85);
2185
2187
  }
2186
- if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModRelax)) {
2188
+ if (this.mods.some((m) => m instanceof osuBase.ModRelax)) {
2187
2189
  // Graph: https://www.desmos.com/calculator/bc9eybdthb
2188
2190
  // We use OD13.3 as maximum since it's the value at which great hit window becomes 0.
2189
2191
  const n100Multiplier = Math.max(0, this.difficultyAttributes.overallDifficulty > 0
@@ -2512,12 +2514,12 @@ class DroidPerformanceCalculator extends PerformanceCalculator {
2512
2514
  * Calculates the accuracy performance value of the beatmap.
2513
2515
  */
2514
2516
  calculateAccuracyValue() {
2515
- if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModRelax) ||
2517
+ if (this.mods.some((m) => m instanceof osuBase.ModRelax) ||
2516
2518
  this.totalSuccessfulHits === 0) {
2517
2519
  return 0;
2518
2520
  }
2519
2521
  let accuracyValue = 650 * Math.exp(-0.1 * this._deviation);
2520
- const ncircles = this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModScoreV2)
2522
+ const ncircles = this.mods.some((m) => m instanceof osuBase.ModScoreV2)
2521
2523
  ? this.totalHits - this.difficultyAttributes.spinnerCount
2522
2524
  : this.difficultyAttributes.hitCircleCount;
2523
2525
  // Bonus for many hitcircles - it's harder to keep good accuracy up for longer.
@@ -2529,7 +2531,7 @@ class DroidPerformanceCalculator extends PerformanceCalculator {
2529
2531
  Math.exp(-(this.difficultyAttributes.rhythmDifficulty - 1) / 2));
2530
2532
  // Penalize accuracy pp after the first miss.
2531
2533
  accuracyValue *= Math.pow(0.97, Math.max(0, this.effectiveMissCount - 1));
2532
- if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModFlashlight)) {
2534
+ if (this.mods.some((m) => m instanceof osuBase.ModFlashlight)) {
2533
2535
  accuracyValue *= 1.02;
2534
2536
  }
2535
2537
  return accuracyValue;
@@ -2538,7 +2540,7 @@ class DroidPerformanceCalculator extends PerformanceCalculator {
2538
2540
  * Calculates the flashlight performance value of the beatmap.
2539
2541
  */
2540
2542
  calculateFlashlightValue() {
2541
- if (!this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModFlashlight)) {
2543
+ if (!this.mods.some((m) => m instanceof osuBase.ModFlashlight)) {
2542
2544
  return 0;
2543
2545
  }
2544
2546
  let flashlightValue = Math.pow(this.difficultyAttributes.flashlightDifficulty, 1.6) * 25;
@@ -2761,7 +2763,7 @@ class DroidPerformanceCalculator extends PerformanceCalculator {
2761
2763
  }
2762
2764
  getConvertedHitWindow() {
2763
2765
  const hitWindow300 = new osuBase.OsuHitWindow(this.difficultyAttributes.overallDifficulty).greatWindow;
2764
- if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModPrecise)) {
2766
+ if (this.mods.some((m) => m instanceof osuBase.ModPrecise)) {
2765
2767
  return new osuBase.PreciseDroidHitWindow(osuBase.PreciseDroidHitWindow.greatWindowToOD(hitWindow300 * this.difficultyAttributes.clockRate));
2766
2768
  }
2767
2769
  else {
@@ -3638,7 +3640,7 @@ class OsuPerformanceCalculator extends PerformanceCalculator {
3638
3640
  aimValue *= lengthBonus;
3639
3641
  aimValue *= this.calculateStrainBasedMissPenalty(this.difficultyAttributes.aimDifficultStrainCount);
3640
3642
  const calculatedAR = this.difficultyAttributes.approachRate;
3641
- if (!this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModRelax)) {
3643
+ if (!this.mods.some((m) => m instanceof osuBase.ModRelax)) {
3642
3644
  // AR scaling
3643
3645
  let arFactor = 0;
3644
3646
  if (calculatedAR > 10.33) {
@@ -3651,7 +3653,7 @@ class OsuPerformanceCalculator extends PerformanceCalculator {
3651
3653
  aimValue *= 1 + arFactor * lengthBonus;
3652
3654
  }
3653
3655
  // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
3654
- if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModHidden)) {
3656
+ if (this.mods.some((m) => m instanceof osuBase.ModHidden)) {
3655
3657
  aimValue *= 1 + 0.04 * (12 - calculatedAR);
3656
3658
  }
3657
3659
  // Scale the aim value with slider factor to nerf very likely dropped sliderends.
@@ -3667,7 +3669,7 @@ class OsuPerformanceCalculator extends PerformanceCalculator {
3667
3669
  * Calculates the speed performance value of the beatmap.
3668
3670
  */
3669
3671
  calculateSpeedValue() {
3670
- if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModRelax)) {
3672
+ if (this.mods.some((m) => m instanceof osuBase.ModRelax)) {
3671
3673
  return 0;
3672
3674
  }
3673
3675
  let speedValue = this.baseValue(this.difficultyAttributes.speedDifficulty);
@@ -3684,7 +3686,7 @@ class OsuPerformanceCalculator extends PerformanceCalculator {
3684
3686
  // Buff for longer maps with high AR.
3685
3687
  speedValue *= 1 + 0.3 * (calculatedAR - 10.33) * lengthBonus;
3686
3688
  }
3687
- if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModHidden)) {
3689
+ if (this.mods.some((m) => m instanceof osuBase.ModHidden)) {
3688
3690
  speedValue *= 1 + 0.04 * (12 - calculatedAR);
3689
3691
  }
3690
3692
  // Calculate accuracy assuming the worst case scenario.
@@ -3717,10 +3719,10 @@ class OsuPerformanceCalculator extends PerformanceCalculator {
3717
3719
  * Calculates the accuracy performance value of the beatmap.
3718
3720
  */
3719
3721
  calculateAccuracyValue() {
3720
- if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModRelax)) {
3722
+ if (this.mods.some((m) => m instanceof osuBase.ModRelax)) {
3721
3723
  return 0;
3722
3724
  }
3723
- const ncircles = this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModScoreV2)
3725
+ const ncircles = this.mods.some((m) => m instanceof osuBase.ModScoreV2)
3724
3726
  ? this.totalHits - this.difficultyAttributes.spinnerCount
3725
3727
  : this.difficultyAttributes.hitCircleCount;
3726
3728
  if (ncircles === 0) {
@@ -3735,10 +3737,10 @@ class OsuPerformanceCalculator extends PerformanceCalculator {
3735
3737
  2.83;
3736
3738
  // Bonus for many hitcircles - it's harder to keep good accuracy up for longer
3737
3739
  accuracyValue *= Math.min(1.15, Math.pow(ncircles / 1000, 0.3));
3738
- if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModHidden)) {
3740
+ if (this.mods.some((m) => m instanceof osuBase.ModHidden)) {
3739
3741
  accuracyValue *= 1.08;
3740
3742
  }
3741
- if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModFlashlight)) {
3743
+ if (this.mods.some((m) => m instanceof osuBase.ModFlashlight)) {
3742
3744
  accuracyValue *= 1.02;
3743
3745
  }
3744
3746
  return accuracyValue;
@@ -3747,7 +3749,7 @@ class OsuPerformanceCalculator extends PerformanceCalculator {
3747
3749
  * Calculates the flashlight performance value of the beatmap.
3748
3750
  */
3749
3751
  calculateFlashlightValue() {
3750
- if (!this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModFlashlight)) {
3752
+ if (!this.mods.some((m) => m instanceof osuBase.ModFlashlight)) {
3751
3753
  return 0;
3752
3754
  }
3753
3755
  let flashlightValue = Math.pow(this.difficultyAttributes.flashlightDifficulty, 2) * 25;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rian8337/osu-difficulty-calculator",
3
- "version": "4.0.0-beta.44",
3
+ "version": "4.0.0-beta.48",
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",
@@ -33,10 +33,10 @@
33
33
  "url": "https://github.com/Rian8337/osu-droid-module/issues"
34
34
  },
35
35
  "dependencies": {
36
- "@rian8337/osu-base": "^4.0.0-beta.44"
36
+ "@rian8337/osu-base": "^4.0.0-beta.48"
37
37
  },
38
38
  "publishConfig": {
39
39
  "access": "public"
40
40
  },
41
- "gitHead": "1a6680610da1128d0986580501c392adc649c948"
41
+ "gitHead": "da34d3703153595c0314e3a6bb62a0b989bae53a"
42
42
  }
@@ -1006,7 +1006,11 @@ declare abstract class PerformanceCalculator<T extends DifficultyAttributes> {
1006
1006
  /**
1007
1007
  * The difficulty attributes that is being calculated.
1008
1008
  */
1009
- readonly difficultyAttributes: T;
1009
+ readonly difficultyAttributes: T | CacheableDifficultyAttributes<T>;
1010
+ /**
1011
+ * The mods that were used.
1012
+ */
1013
+ protected readonly mods: Mod[];
1010
1014
  /**
1011
1015
  * The global multiplier to be applied to the final performance value.
1012
1016
  *
@@ -1254,8 +1258,10 @@ declare class DroidRhythm extends DroidSkill {
1254
1258
  protected readonly reducedSectionBaseline = 0.75;
1255
1259
  protected readonly strainDecayBase = 0.3;
1256
1260
  protected readonly starsPerDouble = 1.75;
1261
+ private readonly useSliderAccuracy;
1257
1262
  private currentRhythmStrain;
1258
1263
  private currentRhythmMultiplier;
1264
+ constructor(mods: Mod[]);
1259
1265
  protected strainValueAt(current: DroidDifficultyHitObject): number;
1260
1266
  protected calculateInitialStrain(time: number, current: DroidDifficultyHitObject): number;
1261
1267
  protected getObjectStrain(): number;
@@ -1275,8 +1281,9 @@ declare abstract class DroidRhythmEvaluator {
1275
1281
  * with historic data of the current object.
1276
1282
  *
1277
1283
  * @param current The current object.
1284
+ * @param useSliderAccuracy Whether to use slider accuracy.
1278
1285
  */
1279
- static evaluateDifficultyOf(current: DroidDifficultyHitObject): number;
1286
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, useSliderAccuracy: boolean): number;
1280
1287
  }
1281
1288
 
1282
1289
  /**