@rian8337/osu-base 4.0.0-beta.71 → 4.0.0-beta.73

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.
Files changed (3) hide show
  1. package/dist/index.js +218 -370
  2. package/package.json +2 -2
  3. package/typings/index.d.ts +154 -122
package/dist/index.js CHANGED
@@ -1535,6 +1535,12 @@ class Mod {
1535
1535
  facilitatesAdjustment() {
1536
1536
  return "facilitateAdjustment" in this;
1537
1537
  }
1538
+ /**
1539
+ * Whether this `Mod` requires the original `Beatmap` for conversion and processing.
1540
+ */
1541
+ requiresOriginalBeatmap() {
1542
+ return "applyFromBeatmap" in this;
1543
+ }
1538
1544
  /**
1539
1545
  * Serializes the settings of this `Mod` to an object that can be converted to a JSON.
1540
1546
  *
@@ -2207,27 +2213,15 @@ class ModFreezeFrame extends Mod {
2207
2213
  super();
2208
2214
  this.name = "Freeze Frame";
2209
2215
  this.acronym = "FR";
2216
+ this.droidRanked = false;
2217
+ this.isDroidRelevant = true;
2218
+ this.droidScoreMultiplier = 1;
2219
+ this.osuRanked = false;
2220
+ this.isOsuRelevant = true;
2221
+ this.osuScoreMultiplier = 1;
2210
2222
  this.lastNewComboTime = 0;
2211
2223
  this.incompatibleMods.add(ModApproachDifferent);
2212
2224
  }
2213
- get droidRanked() {
2214
- return false;
2215
- }
2216
- get isDroidRelevant() {
2217
- return true;
2218
- }
2219
- calculateDroidScoreMultiplier() {
2220
- return 1;
2221
- }
2222
- get osuRanked() {
2223
- return false;
2224
- }
2225
- get isOsuRelevant() {
2226
- return true;
2227
- }
2228
- get osuScoreMultiplier() {
2229
- return 1;
2230
- }
2231
2225
  applyToBeatmap(beatmap) {
2232
2226
  this.lastNewComboTime = 0;
2233
2227
  for (const hitObject of beatmap.hitObjects) {
@@ -2257,21 +2251,13 @@ class ModTraceable extends Mod {
2257
2251
  this.acronym = "TC";
2258
2252
  this.name = "Traceable";
2259
2253
  this.droidRanked = false;
2260
- this.osuRanked = false;
2254
+ this.isDroidRelevant = true;
2255
+ this.droidScoreMultiplier = 1.06;
2256
+ this.osuRanked = true;
2257
+ this.isOsuRelevant = true;
2258
+ this.osuScoreMultiplier = 1;
2261
2259
  this.incompatibleMods.add(ModHidden);
2262
2260
  }
2263
- get isDroidRelevant() {
2264
- return true;
2265
- }
2266
- calculateDroidScoreMultiplier() {
2267
- return 1.06;
2268
- }
2269
- get isOsuRelevant() {
2270
- return true;
2271
- }
2272
- get osuScoreMultiplier() {
2273
- return 1;
2274
- }
2275
2261
  }
2276
2262
 
2277
2263
  /**
@@ -2298,6 +2284,8 @@ class ModHidden extends Mod {
2298
2284
  super();
2299
2285
  this.acronym = "HD";
2300
2286
  this.name = "Hidden";
2287
+ this.isDroidRelevant = true;
2288
+ this.isOsuRelevant = true;
2301
2289
  this.bitwise = 1 << 3;
2302
2290
  /**
2303
2291
  * Whether to only fade approach circles.
@@ -2307,17 +2295,11 @@ class ModHidden extends Mod {
2307
2295
  this.onlyFadeApproachCircles = new BooleanModSetting("Only fade approach circles", "The main object body will not fade when enabled.", false);
2308
2296
  this.incompatibleMods.add(ModTraceable).add(ModApproachDifferent);
2309
2297
  }
2310
- get isDroidRelevant() {
2311
- return true;
2312
- }
2313
- calculateDroidScoreMultiplier() {
2314
- return 1.06;
2315
- }
2316
- get isOsuRelevant() {
2317
- return true;
2298
+ get droidScoreMultiplier() {
2299
+ return this.usesDefaultSettings ? 1.06 : 1;
2318
2300
  }
2319
2301
  get osuScoreMultiplier() {
2320
- return 1.06;
2302
+ return this.usesDefaultSettings ? 1.06 : 1;
2321
2303
  }
2322
2304
  copySettings(mod) {
2323
2305
  var _a, _b;
@@ -2498,6 +2480,12 @@ class ModApproachDifferent extends Mod {
2498
2480
  super();
2499
2481
  this.name = "Approach Different";
2500
2482
  this.acronym = "AD";
2483
+ this.droidRanked = false;
2484
+ this.droidScoreMultiplier = 1;
2485
+ this.isDroidRelevant = true;
2486
+ this.osuRanked = false;
2487
+ this.isOsuRelevant = true;
2488
+ this.osuScoreMultiplier = 1;
2501
2489
  /**
2502
2490
  * The initial size of the approach circle, relative to hit circles.
2503
2491
  */
@@ -2508,24 +2496,6 @@ class ModApproachDifferent extends Mod {
2508
2496
  this.style = new ModSetting("Style", "The animation style of the approach circles.", exports.AnimationStyle.gravity);
2509
2497
  this.incompatibleMods.add(ModHidden).add(ModFreezeFrame);
2510
2498
  }
2511
- get droidRanked() {
2512
- return false;
2513
- }
2514
- get isDroidRelevant() {
2515
- return true;
2516
- }
2517
- calculateDroidScoreMultiplier() {
2518
- return 1;
2519
- }
2520
- get osuRanked() {
2521
- return false;
2522
- }
2523
- get isOsuRelevant() {
2524
- return true;
2525
- }
2526
- get osuScoreMultiplier() {
2527
- return 1;
2528
- }
2529
2499
  copySettings(mod) {
2530
2500
  super.copySettings(mod);
2531
2501
  const { settings } = mod;
@@ -2566,22 +2536,14 @@ class ModRelax extends Mod {
2566
2536
  this.acronym = "RX";
2567
2537
  this.name = "Relax";
2568
2538
  this.droidRanked = false;
2539
+ this.isDroidRelevant = true;
2540
+ this.droidScoreMultiplier = 0.001;
2569
2541
  this.osuRanked = false;
2542
+ this.isOsuRelevant = true;
2543
+ this.osuScoreMultiplier = 0;
2570
2544
  this.bitwise = 1 << 7;
2571
2545
  this.incompatibleMods.add(ModAuto).add(ModAutopilot);
2572
2546
  }
2573
- get isDroidRelevant() {
2574
- return true;
2575
- }
2576
- calculateDroidScoreMultiplier() {
2577
- return 0.001;
2578
- }
2579
- get isOsuRelevant() {
2580
- return true;
2581
- }
2582
- get osuScoreMultiplier() {
2583
- return 0;
2584
- }
2585
2547
  }
2586
2548
 
2587
2549
  /**
@@ -2593,22 +2555,14 @@ class ModAutopilot extends Mod {
2593
2555
  this.acronym = "AP";
2594
2556
  this.name = "Autopilot";
2595
2557
  this.droidRanked = false;
2558
+ this.isDroidRelevant = true;
2559
+ this.droidScoreMultiplier = 0.001;
2596
2560
  this.osuRanked = false;
2561
+ this.isOsuRelevant = true;
2562
+ this.osuScoreMultiplier = 0;
2597
2563
  this.bitwise = 1 << 13;
2598
2564
  this.incompatibleMods.add(ModRelax).add(ModAuto);
2599
2565
  }
2600
- get isDroidRelevant() {
2601
- return true;
2602
- }
2603
- calculateDroidScoreMultiplier() {
2604
- return 0.001;
2605
- }
2606
- get isOsuRelevant() {
2607
- return true;
2608
- }
2609
- get osuScoreMultiplier() {
2610
- return 0;
2611
- }
2612
2566
  }
2613
2567
 
2614
2568
  /**
@@ -2620,22 +2574,14 @@ class ModAuto extends Mod {
2620
2574
  this.acronym = "AT";
2621
2575
  this.name = "Autoplay";
2622
2576
  this.droidRanked = false;
2577
+ this.isDroidRelevant = true;
2578
+ this.droidScoreMultiplier = 1;
2623
2579
  this.osuRanked = false;
2580
+ this.isOsuRelevant = true;
2581
+ this.osuScoreMultiplier = 1;
2624
2582
  this.bitwise = 1 << 11;
2625
2583
  this.incompatibleMods.add(ModAutopilot).add(ModRelax);
2626
2584
  }
2627
- get isDroidRelevant() {
2628
- return true;
2629
- }
2630
- calculateDroidScoreMultiplier() {
2631
- return 1;
2632
- }
2633
- get isOsuRelevant() {
2634
- return true;
2635
- }
2636
- get osuScoreMultiplier() {
2637
- return 1;
2638
- }
2639
2585
  }
2640
2586
 
2641
2587
  /**
@@ -2687,8 +2633,8 @@ class ModCustomSpeed extends ModRateAdjust {
2687
2633
  get isDroidRelevant() {
2688
2634
  return this.isRelevant;
2689
2635
  }
2690
- calculateDroidScoreMultiplier() {
2691
- return this.droidScoreMultiplier;
2636
+ get droidScoreMultiplier() {
2637
+ return super.droidScoreMultiplier;
2692
2638
  }
2693
2639
  get isOsuRelevant() {
2694
2640
  return this.isRelevant;
@@ -2742,26 +2688,14 @@ class ModDeflate extends ModObjectScaleTween {
2742
2688
  super(...arguments);
2743
2689
  this.name = "Deflate";
2744
2690
  this.acronym = "DF";
2691
+ this.droidRanked = false;
2692
+ this.isDroidRelevant = true;
2693
+ this.droidScoreMultiplier = 1;
2694
+ this.osuRanked = false;
2695
+ this.isOsuRelevant = true;
2696
+ this.osuScoreMultiplier = 1;
2745
2697
  this.startScale = new DecimalModSetting("Start scale", "The initial size multiplier applied to all hit objects.", 2, 1, 25, 0.1, 1);
2746
2698
  }
2747
- get droidRanked() {
2748
- return false;
2749
- }
2750
- get isDroidRelevant() {
2751
- return true;
2752
- }
2753
- calculateDroidScoreMultiplier() {
2754
- return 1;
2755
- }
2756
- get osuRanked() {
2757
- return false;
2758
- }
2759
- get isOsuRelevant() {
2760
- return true;
2761
- }
2762
- get osuScoreMultiplier() {
2763
- return 1;
2764
- }
2765
2699
  }
2766
2700
 
2767
2701
  /**
@@ -3985,25 +3919,17 @@ class ModMirror extends Mod {
3985
3919
  this.name = "Mirror";
3986
3920
  this.acronym = "MR";
3987
3921
  this.droidRanked = false;
3922
+ this.isDroidRelevant = true;
3923
+ this.droidScoreMultiplier = 1;
3988
3924
  this.osuRanked = false;
3925
+ this.isOsuRelevant = true;
3926
+ this.osuScoreMultiplier = 1;
3989
3927
  /**
3990
3928
  * The axes to reflect the `HitObject`s along.
3991
3929
  */
3992
3930
  this.flippedAxes = new ModSetting("Flipped axes", "The axes to reflect the hit objects along.", exports.Axes.x);
3993
3931
  this.incompatibleMods.add(ModHardRock);
3994
3932
  }
3995
- get isDroidRelevant() {
3996
- return true;
3997
- }
3998
- calculateDroidScoreMultiplier() {
3999
- return 1;
4000
- }
4001
- get isOsuRelevant() {
4002
- return true;
4003
- }
4004
- get osuScoreMultiplier() {
4005
- return 1;
4006
- }
4007
3933
  copySettings(mod) {
4008
3934
  var _a;
4009
3935
  super.copySettings(mod);
@@ -4066,14 +3992,10 @@ class ModReplayV6 extends Mod {
4066
3992
  this.acronym = "RV6";
4067
3993
  this.userPlayable = false;
4068
3994
  this.droidRanked = false;
3995
+ this.isDroidRelevant = true;
3996
+ this.droidScoreMultiplier = 1;
4069
3997
  this.facilitateAdjustment = true;
4070
3998
  }
4071
- get isDroidRelevant() {
4072
- return true;
4073
- }
4074
- calculateDroidScoreMultiplier() {
4075
- return 1;
4076
- }
4077
3999
  applyToBeatmap(beatmap) {
4078
4000
  const { objects } = beatmap.hitObjects;
4079
4001
  if (objects.length === 0) {
@@ -4116,23 +4038,15 @@ class ModHardRock extends Mod {
4116
4038
  this.acronym = "HR";
4117
4039
  this.name = "HardRock";
4118
4040
  this.droidRanked = true;
4041
+ this.isDroidRelevant = true;
4042
+ this.droidScoreMultiplier = 1.06;
4119
4043
  this.osuRanked = true;
4044
+ this.isOsuRelevant = true;
4045
+ this.osuScoreMultiplier = 1.06;
4120
4046
  this.bitwise = 1 << 4;
4121
4047
  this.incompatibleMods.add(ModEasy);
4122
4048
  this.incompatibleMods.add(ModMirror);
4123
4049
  }
4124
- get isDroidRelevant() {
4125
- return true;
4126
- }
4127
- calculateDroidScoreMultiplier() {
4128
- return 1.06;
4129
- }
4130
- get isOsuRelevant() {
4131
- return true;
4132
- }
4133
- get osuScoreMultiplier() {
4134
- return 1.06;
4135
- }
4136
4050
  applyToDifficulty(mode, difficulty, adjustmentMods) {
4137
4051
  if (mode === exports.Modes.osu || !adjustmentMods.has(ModReplayV6)) {
4138
4052
  difficulty.cs = this.applySetting(difficulty.cs, 1.3);
@@ -4173,22 +4087,14 @@ class ModEasy extends Mod {
4173
4087
  this.acronym = "EZ";
4174
4088
  this.name = "Easy";
4175
4089
  this.droidRanked = true;
4090
+ this.isDroidRelevant = true;
4091
+ this.droidScoreMultiplier = 0.5;
4176
4092
  this.osuRanked = true;
4093
+ this.isOsuRelevant = true;
4094
+ this.osuScoreMultiplier = 0.5;
4177
4095
  this.bitwise = 1 << 1;
4178
4096
  this.incompatibleMods.add(ModHardRock);
4179
4097
  }
4180
- get isDroidRelevant() {
4181
- return true;
4182
- }
4183
- calculateDroidScoreMultiplier() {
4184
- return 0.5;
4185
- }
4186
- get isOsuRelevant() {
4187
- return true;
4188
- }
4189
- get osuScoreMultiplier() {
4190
- return 0.5;
4191
- }
4192
4098
  applyToDifficulty(mode, difficulty, adjustmentMods) {
4193
4099
  if (mode === exports.Modes.osu || !adjustmentMods.has(ModReplayV6)) {
4194
4100
  difficulty.cs /= 2;
@@ -4223,12 +4129,8 @@ class ModReallyEasy extends Mod {
4223
4129
  this.acronym = "RE";
4224
4130
  this.name = "Really Easy";
4225
4131
  this.droidRanked = false;
4226
- }
4227
- get isDroidRelevant() {
4228
- return true;
4229
- }
4230
- calculateDroidScoreMultiplier() {
4231
- return 0.4;
4132
+ this.isDroidRelevant = true;
4133
+ this.droidScoreMultiplier = 0.4;
4232
4134
  }
4233
4135
  applyToDifficultyWithMods(mode, difficulty, mods) {
4234
4136
  var _a;
@@ -4285,12 +4187,8 @@ class ModSmallCircle extends Mod {
4285
4187
  this.acronym = "SC";
4286
4188
  this.name = "SmallCircle";
4287
4189
  this.droidRanked = false;
4288
- }
4289
- get isDroidRelevant() {
4290
- return true;
4291
- }
4292
- calculateDroidScoreMultiplier() {
4293
- return 1.06;
4190
+ this.isDroidRelevant = true;
4191
+ this.droidScoreMultiplier = 1;
4294
4192
  }
4295
4193
  migrateDroidMod(difficulty) {
4296
4194
  return new ModDifficultyAdjust({ cs: difficulty.cs + 4 });
@@ -4387,6 +4285,8 @@ class ModDifficultyAdjust extends Mod {
4387
4285
  this.name = "Difficulty Adjust";
4388
4286
  this.droidRanked = false;
4389
4287
  this.osuRanked = false;
4288
+ this.isOsuRelevant = true;
4289
+ this.osuScoreMultiplier = 1;
4390
4290
  /**
4391
4291
  * The circle size to enforce.
4392
4292
  */
@@ -4403,10 +4303,10 @@ class ModDifficultyAdjust extends Mod {
4403
4303
  * The health drain rate to enforce.
4404
4304
  */
4405
4305
  this.hp = new NullableDecimalModSetting("Health drain", "The health drain to enforce.", null, 0, 11, 0.1, 1);
4406
- this.cs.value = (_a = values === null || values === void 0 ? void 0 : values.cs) !== null && _a !== void 0 ? _a : null;
4407
- this.ar.value = (_b = values === null || values === void 0 ? void 0 : values.ar) !== null && _b !== void 0 ? _b : null;
4408
- this.od.value = (_c = values === null || values === void 0 ? void 0 : values.od) !== null && _c !== void 0 ? _c : null;
4409
- this.hp.value = (_d = values === null || values === void 0 ? void 0 : values.hp) !== null && _d !== void 0 ? _d : null;
4306
+ this.cs.value = this.cs.defaultValue = (_a = values === null || values === void 0 ? void 0 : values.cs) !== null && _a !== void 0 ? _a : null;
4307
+ this.ar.value = this.ar.defaultValue = (_b = values === null || values === void 0 ? void 0 : values.ar) !== null && _b !== void 0 ? _b : null;
4308
+ this.od.value = this.od.defaultValue = (_c = values === null || values === void 0 ? void 0 : values.od) !== null && _c !== void 0 ? _c : null;
4309
+ this.hp.value = this.hp.defaultValue = (_d = values === null || values === void 0 ? void 0 : values.hp) !== null && _d !== void 0 ? _d : null;
4410
4310
  }
4411
4311
  copySettings(mod) {
4412
4312
  var _a, _b, _c, _d, _e, _f, _g, _h;
@@ -4419,18 +4319,18 @@ class ModDifficultyAdjust extends Mod {
4419
4319
  get isDroidRelevant() {
4420
4320
  return this.isRelevant;
4421
4321
  }
4422
- calculateDroidScoreMultiplier(difficulty) {
4322
+ get droidScoreMultiplier() {
4423
4323
  // Graph: https://www.desmos.com/calculator/yrggkhrkzz
4424
4324
  let multiplier = 1;
4425
- if (this.cs.value !== null) {
4426
- const diff = this.cs.value - difficulty.cs;
4325
+ if (this.cs.value !== null && this.cs.defaultValue !== null) {
4326
+ const diff = this.cs.value - this.cs.defaultValue;
4427
4327
  multiplier *=
4428
4328
  diff >= 0
4429
4329
  ? 1 + 0.0075 * Math.pow(diff, 1.5)
4430
4330
  : 2 / (1 + Math.exp(-0.5 * diff));
4431
4331
  }
4432
- if (this.od.value !== null) {
4433
- const diff = this.od.value - difficulty.od;
4332
+ if (this.od.value !== null && this.od.defaultValue !== null) {
4333
+ const diff = this.od.value - this.od.defaultValue;
4434
4334
  multiplier *=
4435
4335
  diff >= 0
4436
4336
  ? 1 + 0.005 * Math.pow(diff, 1.3)
@@ -4438,11 +4338,11 @@ class ModDifficultyAdjust extends Mod {
4438
4338
  }
4439
4339
  return multiplier;
4440
4340
  }
4441
- get isOsuRelevant() {
4442
- return this.isRelevant;
4443
- }
4444
- get osuScoreMultiplier() {
4445
- return 0.5;
4341
+ applyFromBeatmap(beatmap) {
4342
+ this.cs.defaultValue = beatmap.difficulty.cs;
4343
+ this.ar.defaultValue = beatmap.difficulty.ar;
4344
+ this.od.defaultValue = beatmap.difficulty.od;
4345
+ this.hp.defaultValue = beatmap.difficulty.hp;
4446
4346
  }
4447
4347
  applyToDifficultyWithMods(_, difficulty, mods) {
4448
4348
  var _a, _b, _c, _d;
@@ -4555,21 +4455,19 @@ class ModNightCore extends ModRateAdjust {
4555
4455
  this.name = "NightCore";
4556
4456
  this.droidRanked = true;
4557
4457
  this.osuRanked = true;
4458
+ this.osuScoreMultiplier = 1.12;
4558
4459
  this.bitwise = 1 << 9;
4559
4460
  this.incompatibleMods.add(ModDoubleTime).add(ModHalfTime);
4560
4461
  }
4561
4462
  get isDroidRelevant() {
4562
4463
  return this.isRelevant;
4563
4464
  }
4564
- calculateDroidScoreMultiplier() {
4565
- return this.droidScoreMultiplier;
4465
+ get droidScoreMultiplier() {
4466
+ return super.droidScoreMultiplier;
4566
4467
  }
4567
4468
  get isOsuRelevant() {
4568
4469
  return this.isRelevant;
4569
4470
  }
4570
- get osuScoreMultiplier() {
4571
- return 1.12;
4572
- }
4573
4471
  }
4574
4472
 
4575
4473
  /**
@@ -4582,21 +4480,19 @@ class ModHalfTime extends ModRateAdjust {
4582
4480
  this.name = "HalfTime";
4583
4481
  this.droidRanked = true;
4584
4482
  this.osuRanked = true;
4483
+ this.osuScoreMultiplier = 0.3;
4585
4484
  this.bitwise = 1 << 8;
4586
4485
  this.incompatibleMods.add(ModDoubleTime).add(ModNightCore);
4587
4486
  }
4588
4487
  get isDroidRelevant() {
4589
4488
  return this.isRelevant;
4590
4489
  }
4591
- calculateDroidScoreMultiplier() {
4592
- return this.droidScoreMultiplier;
4490
+ get droidScoreMultiplier() {
4491
+ return super.droidScoreMultiplier;
4593
4492
  }
4594
4493
  get isOsuRelevant() {
4595
4494
  return this.isRelevant;
4596
4495
  }
4597
- get osuScoreMultiplier() {
4598
- return 0.3;
4599
- }
4600
4496
  }
4601
4497
 
4602
4498
  /**
@@ -4609,21 +4505,19 @@ class ModDoubleTime extends ModRateAdjust {
4609
4505
  this.name = "DoubleTime";
4610
4506
  this.droidRanked = true;
4611
4507
  this.osuRanked = true;
4508
+ this.osuScoreMultiplier = 1.12;
4612
4509
  this.bitwise = 1 << 6;
4613
4510
  this.incompatibleMods.add(ModHalfTime).add(ModNightCore);
4614
4511
  }
4615
4512
  get isDroidRelevant() {
4616
4513
  return this.isRelevant;
4617
4514
  }
4618
- calculateDroidScoreMultiplier() {
4619
- return this.droidScoreMultiplier;
4515
+ get droidScoreMultiplier() {
4516
+ return super.droidScoreMultiplier;
4620
4517
  }
4621
4518
  get isOsuRelevant() {
4622
4519
  return this.isRelevant;
4623
4520
  }
4624
- get osuScoreMultiplier() {
4625
- return 1.12;
4626
- }
4627
4521
  }
4628
4522
 
4629
4523
  /**
@@ -4635,7 +4529,11 @@ class ModFlashlight extends Mod {
4635
4529
  this.acronym = "FL";
4636
4530
  this.name = "Flashlight";
4637
4531
  this.droidRanked = true;
4532
+ this.isDroidRelevant = true;
4533
+ this.droidScoreMultiplier = 1.12;
4638
4534
  this.osuRanked = true;
4535
+ this.isOsuRelevant = true;
4536
+ this.osuScoreMultiplier = 1.12;
4639
4537
  this.bitwise = 1 << 10;
4640
4538
  /**
4641
4539
  * The amount of seconds until the Flashlight follow area reaches the cursor.
@@ -4648,18 +4546,6 @@ class ModFlashlight extends Mod {
4648
4546
  this.followDelay.value =
4649
4547
  (_b = (_a = mod.settings) === null || _a === void 0 ? void 0 : _a.areaFollowDelay) !== null && _b !== void 0 ? _b : this.followDelay.value;
4650
4548
  }
4651
- get isDroidRelevant() {
4652
- return true;
4653
- }
4654
- calculateDroidScoreMultiplier() {
4655
- return 1.12;
4656
- }
4657
- get isOsuRelevant() {
4658
- return true;
4659
- }
4660
- get osuScoreMultiplier() {
4661
- return 1.12;
4662
- }
4663
4549
  serializeSettings() {
4664
4550
  return { areaFollowDelay: this.followDelay.value };
4665
4551
  }
@@ -4683,26 +4569,14 @@ class ModGrow extends ModObjectScaleTween {
4683
4569
  super(...arguments);
4684
4570
  this.name = "Grow";
4685
4571
  this.acronym = "GR";
4572
+ this.droidRanked = false;
4573
+ this.isDroidRelevant = true;
4574
+ this.droidScoreMultiplier = 1;
4575
+ this.osuRanked = false;
4576
+ this.isOsuRelevant = true;
4577
+ this.osuScoreMultiplier = 1;
4686
4578
  this.startScale = new DecimalModSetting("Start scale", "The initial size multiplier applied to all hit objects.", 0.5, 0, 0.99, 0.01, 2);
4687
4579
  }
4688
- get droidRanked() {
4689
- return false;
4690
- }
4691
- get isDroidRelevant() {
4692
- return true;
4693
- }
4694
- calculateDroidScoreMultiplier() {
4695
- return 1;
4696
- }
4697
- get osuRanked() {
4698
- return false;
4699
- }
4700
- get isOsuRelevant() {
4701
- return true;
4702
- }
4703
- get osuScoreMultiplier() {
4704
- return 1;
4705
- }
4706
4580
  }
4707
4581
 
4708
4582
  /**
@@ -4718,28 +4592,16 @@ class IntegerModSetting extends NumberModSetting {
4718
4592
  * Represents the Muted mod.
4719
4593
  */
4720
4594
  class ModMuted extends Mod {
4721
- get droidRanked() {
4722
- return false;
4723
- }
4724
- get isDroidRelevant() {
4725
- return true;
4726
- }
4727
- calculateDroidScoreMultiplier() {
4728
- return 1;
4729
- }
4730
- get osuRanked() {
4731
- return true;
4732
- }
4733
- get isOsuRelevant() {
4734
- return true;
4735
- }
4736
- get osuScoreMultiplier() {
4737
- return 1;
4738
- }
4739
4595
  constructor() {
4740
4596
  super();
4741
4597
  this.name = "Muted";
4742
4598
  this.acronym = "MU";
4599
+ this.droidRanked = false;
4600
+ this.isDroidRelevant = true;
4601
+ this.droidScoreMultiplier = 1;
4602
+ this.osuRanked = true;
4603
+ this.isOsuRelevant = true;
4604
+ this.osuScoreMultiplier = 1;
4743
4605
  /**
4744
4606
  * Increase volume as combo builds.
4745
4607
  */
@@ -4802,22 +4664,14 @@ class ModSuddenDeath extends Mod {
4802
4664
  this.acronym = "SD";
4803
4665
  this.name = "Sudden Death";
4804
4666
  this.droidRanked = true;
4667
+ this.isDroidRelevant = true;
4668
+ this.droidScoreMultiplier = 1;
4805
4669
  this.osuRanked = true;
4670
+ this.isOsuRelevant = true;
4671
+ this.osuScoreMultiplier = 1;
4806
4672
  this.bitwise = 1 << 5;
4807
4673
  this.incompatibleMods.add(ModNoFail).add(ModPerfect);
4808
4674
  }
4809
- get isDroidRelevant() {
4810
- return true;
4811
- }
4812
- calculateDroidScoreMultiplier() {
4813
- return 1;
4814
- }
4815
- get isOsuRelevant() {
4816
- return true;
4817
- }
4818
- get osuScoreMultiplier() {
4819
- return 1;
4820
- }
4821
4675
  }
4822
4676
 
4823
4677
  /**
@@ -4829,22 +4683,14 @@ class ModPerfect extends Mod {
4829
4683
  this.acronym = "PF";
4830
4684
  this.name = "Perfect";
4831
4685
  this.droidRanked = true;
4686
+ this.isDroidRelevant = true;
4687
+ this.droidScoreMultiplier = 1;
4832
4688
  this.osuRanked = true;
4689
+ this.isOsuRelevant = true;
4690
+ this.osuScoreMultiplier = 1;
4833
4691
  this.bitwise = 1 << 14;
4834
4692
  this.incompatibleMods.add(ModNoFail).add(ModSuddenDeath);
4835
4693
  }
4836
- get isDroidRelevant() {
4837
- return true;
4838
- }
4839
- calculateDroidScoreMultiplier() {
4840
- return 1;
4841
- }
4842
- get isOsuRelevant() {
4843
- return true;
4844
- }
4845
- get osuScoreMultiplier() {
4846
- return 1;
4847
- }
4848
4694
  }
4849
4695
 
4850
4696
  /**
@@ -4856,22 +4702,14 @@ class ModNoFail extends Mod {
4856
4702
  this.acronym = "NF";
4857
4703
  this.name = "NoFail";
4858
4704
  this.droidRanked = true;
4705
+ this.isDroidRelevant = true;
4706
+ this.droidScoreMultiplier = 0.5;
4859
4707
  this.osuRanked = true;
4708
+ this.isOsuRelevant = true;
4709
+ this.osuScoreMultiplier = 0.5;
4860
4710
  this.bitwise = 1 << 0;
4861
4711
  this.incompatibleMods.add(ModPerfect).add(ModSuddenDeath);
4862
4712
  }
4863
- get isDroidRelevant() {
4864
- return true;
4865
- }
4866
- calculateDroidScoreMultiplier() {
4867
- return 0.5;
4868
- }
4869
- get isOsuRelevant() {
4870
- return true;
4871
- }
4872
- get osuScoreMultiplier() {
4873
- return 0.5;
4874
- }
4875
4713
  }
4876
4714
 
4877
4715
  /**
@@ -4883,12 +4721,8 @@ class ModPrecise extends Mod {
4883
4721
  this.acronym = "PR";
4884
4722
  this.name = "Precise";
4885
4723
  this.droidRanked = true;
4886
- }
4887
- get isDroidRelevant() {
4888
- return true;
4889
- }
4890
- calculateDroidScoreMultiplier() {
4891
- return 1.06;
4724
+ this.isDroidRelevant = true;
4725
+ this.droidScoreMultiplier = 1.06;
4892
4726
  }
4893
4727
  applyToHitObject(mode, hitObject) {
4894
4728
  var _a, _b;
@@ -5055,7 +4889,11 @@ class ModRandom extends Mod {
5055
4889
  this.name = "Random";
5056
4890
  this.acronym = "RD";
5057
4891
  this.droidRanked = false;
4892
+ this.isDroidRelevant = true;
4893
+ this.droidScoreMultiplier = 1;
5058
4894
  this.osuRanked = false;
4895
+ this.isOsuRelevant = true;
4896
+ this.osuScoreMultiplier = 1;
5059
4897
  /**
5060
4898
  * The seed to use.
5061
4899
  */
@@ -5066,18 +4904,6 @@ class ModRandom extends Mod {
5066
4904
  this.angleSharpness = new DecimalModSetting("Angle sharpness", "Defines how sharp the angles of hit objects should be.", 7, 1, 10, 0.1, 1);
5067
4905
  this.random = null;
5068
4906
  }
5069
- get isDroidRelevant() {
5070
- return true;
5071
- }
5072
- calculateDroidScoreMultiplier() {
5073
- return 1;
5074
- }
5075
- get isOsuRelevant() {
5076
- return true;
5077
- }
5078
- get osuScoreMultiplier() {
5079
- return 1;
5080
- }
5081
4907
  copySettings(mod) {
5082
4908
  super.copySettings(mod);
5083
4909
  const { settings } = mod;
@@ -5220,21 +5046,13 @@ class ModScoreV2 extends Mod {
5220
5046
  this.acronym = "V2";
5221
5047
  this.name = "ScoreV2";
5222
5048
  this.droidRanked = false;
5049
+ this.isDroidRelevant = true;
5050
+ this.droidScoreMultiplier = 1;
5223
5051
  this.osuRanked = false;
5052
+ this.isOsuRelevant = true;
5053
+ this.osuScoreMultiplier = 1;
5224
5054
  this.bitwise = 1 << 29;
5225
5055
  }
5226
- get isDroidRelevant() {
5227
- return true;
5228
- }
5229
- calculateDroidScoreMultiplier() {
5230
- return 1;
5231
- }
5232
- get isOsuRelevant() {
5233
- return true;
5234
- }
5235
- get osuScoreMultiplier() {
5236
- return 1;
5237
- }
5238
5056
  }
5239
5057
 
5240
5058
  /**
@@ -5265,19 +5083,11 @@ class ModSynesthesia extends Mod {
5265
5083
  this.name = "Synesthesia";
5266
5084
  this.acronym = "SY";
5267
5085
  this.droidRanked = false;
5086
+ this.isDroidRelevant = true;
5087
+ this.droidScoreMultiplier = 0.8;
5268
5088
  this.osuRanked = false;
5269
- }
5270
- get isDroidRelevant() {
5271
- return true;
5272
- }
5273
- calculateDroidScoreMultiplier() {
5274
- return 0.8;
5275
- }
5276
- get isOsuRelevant() {
5277
- return true;
5278
- }
5279
- get osuScoreMultiplier() {
5280
- return 0.8;
5089
+ this.isOsuRelevant = true;
5090
+ this.osuScoreMultiplier = 0.8;
5281
5091
  }
5282
5092
  }
5283
5093
 
@@ -5479,6 +5289,13 @@ class Interpolation {
5479
5289
  * Represents a mod that gradually adjusts the track's playback rate over time.
5480
5290
  */
5481
5291
  class ModTimeRamp extends Mod {
5292
+ /**
5293
+ * The generic osu!droid score multiplier of this `Mod`.
5294
+ */
5295
+ get droidScoreMultiplier() {
5296
+ // Graph: https://www.desmos.com/calculator/1zp4vwl3o7
5297
+ return Interpolation.lerp(this.calculateScoreMultiplierAt(this.initialRate.value), this.calculateScoreMultiplierAt(this.finalRate.value), ModTimeRamp.finalRateProgress);
5298
+ }
5482
5299
  constructor() {
5483
5300
  super();
5484
5301
  this.initialRateTime = 0;
@@ -5505,6 +5322,9 @@ class ModTimeRamp extends Mod {
5505
5322
  return (rate *
5506
5323
  Interpolation.lerp(this.initialRate.value, this.finalRate.value, MathUtils.clamp(amount, 0, 1)));
5507
5324
  }
5325
+ calculateScoreMultiplierAt(rate) {
5326
+ return rate > 1 ? 1 + (rate - 1) * 0.24 : Math.pow(0.3, (1 - rate) * 4);
5327
+ }
5508
5328
  serializeSettings() {
5509
5329
  return {
5510
5330
  initialRate: this.initialRate.value,
@@ -5524,12 +5344,18 @@ ModTimeRamp.finalRateProgress = 0.75;
5524
5344
  * Represents the Wind Down mod.
5525
5345
  */
5526
5346
  class ModWindDown extends ModTimeRamp {
5347
+ get droidScoreMultiplier() {
5348
+ return super.droidScoreMultiplier;
5349
+ }
5527
5350
  constructor() {
5528
5351
  super();
5529
5352
  this.name = "Wind Down";
5530
5353
  this.acronym = "WD";
5531
5354
  this.droidRanked = false;
5355
+ this.isDroidRelevant = true;
5532
5356
  this.osuRanked = false;
5357
+ this.isOsuRelevant = true;
5358
+ this.osuScoreMultiplier = 1;
5533
5359
  this.initialRate = new DecimalModSetting("Initial rate", "The starting speed of the track.", 1, 0.51, 2, 0.01, 2);
5534
5360
  this.finalRate = new DecimalModSetting("Final rate", "The final speed to ramp to.", 0.75, 0.5, 1.99, 0.01, 2);
5535
5361
  this.initialRate.bindValueChanged((value) => {
@@ -5543,30 +5369,24 @@ class ModWindDown extends ModTimeRamp {
5543
5369
  }
5544
5370
  });
5545
5371
  }
5546
- get isDroidRelevant() {
5547
- return true;
5548
- }
5549
- calculateDroidScoreMultiplier() {
5550
- return 1;
5551
- }
5552
- get isOsuRelevant() {
5553
- return true;
5554
- }
5555
- get osuScoreMultiplier() {
5556
- return 1;
5557
- }
5558
5372
  }
5559
5373
 
5560
5374
  /**
5561
5375
  * Represents the Wind Up mod.
5562
5376
  */
5563
5377
  class ModWindUp extends ModTimeRamp {
5378
+ get droidScoreMultiplier() {
5379
+ return super.droidScoreMultiplier;
5380
+ }
5564
5381
  constructor() {
5565
5382
  super();
5566
5383
  this.name = "Wind Up";
5567
5384
  this.acronym = "WU";
5568
5385
  this.droidRanked = false;
5386
+ this.isDroidRelevant = true;
5569
5387
  this.osuRanked = false;
5388
+ this.isOsuRelevant = true;
5389
+ this.osuScoreMultiplier = 1;
5570
5390
  this.initialRate = new DecimalModSetting("Initial rate", "The starting speed of the track.", 1, 0.5, 1.99, 0.01, 2);
5571
5391
  this.finalRate = new DecimalModSetting("Final rate", "The final speed to ramp to.", 1.5, 0.51, 2, 0.01, 2);
5572
5392
  this.initialRate.bindValueChanged((value) => {
@@ -5580,18 +5400,6 @@ class ModWindUp extends ModTimeRamp {
5580
5400
  }
5581
5401
  });
5582
5402
  }
5583
- get isDroidRelevant() {
5584
- return true;
5585
- }
5586
- calculateDroidScoreMultiplier() {
5587
- return 1;
5588
- }
5589
- get isOsuRelevant() {
5590
- return true;
5591
- }
5592
- get osuScoreMultiplier() {
5593
- return 1;
5594
- }
5595
5403
  }
5596
5404
 
5597
5405
  /**
@@ -5617,6 +5425,53 @@ class ModUtil {
5617
5425
  }
5618
5426
  return map;
5619
5427
  }
5428
+ /**
5429
+ * Calculates the score multiplier of the given mods and game mode.
5430
+ *
5431
+ * @param mods The mods to calculate the score multiplier for.
5432
+ * @param mode The game mode to calculate the score multiplier for.
5433
+ * @param beatmap The beatmap the mods are applied to. Needed for some mods to have an effect on
5434
+ * score multiplier (i.e., `ModDifficultyAdjust`).
5435
+ * @returns The score multiplier.
5436
+ */
5437
+ static calculateScoreMultiplier(mods, mode, beatmap) {
5438
+ // Rate-adjusting mods combine their track rate multipliers together, then bunched together.
5439
+ let totalRateAdjustTrackRateMultiplier = 1;
5440
+ let scoreMultiplier = 1;
5441
+ for (const mod of mods) {
5442
+ if (mod.requiresOriginalBeatmap() && beatmap) {
5443
+ mod.applyFromBeatmap(beatmap);
5444
+ }
5445
+ if (mod instanceof ModRateAdjust) {
5446
+ totalRateAdjustTrackRateMultiplier *=
5447
+ mod.trackRateMultiplier.value;
5448
+ }
5449
+ else {
5450
+ switch (mode) {
5451
+ case exports.Modes.droid:
5452
+ if (mod.isApplicableToDroid()) {
5453
+ scoreMultiplier *= mod.droidScoreMultiplier;
5454
+ }
5455
+ break;
5456
+ case exports.Modes.osu:
5457
+ if (mod.isApplicableToOsuStable()) {
5458
+ scoreMultiplier *= mod.osuScoreMultiplier;
5459
+ }
5460
+ break;
5461
+ }
5462
+ }
5463
+ }
5464
+ const customSpeed = new ModCustomSpeed(totalRateAdjustTrackRateMultiplier);
5465
+ switch (mode) {
5466
+ case exports.Modes.droid:
5467
+ scoreMultiplier *= customSpeed.droidScoreMultiplier;
5468
+ break;
5469
+ case exports.Modes.osu:
5470
+ scoreMultiplier *= customSpeed.osuScoreMultiplier;
5471
+ break;
5472
+ }
5473
+ return scoreMultiplier;
5474
+ }
5620
5475
  /**
5621
5476
  * Serializes a list of `Mod`s.
5622
5477
  *
@@ -5637,12 +5492,15 @@ class ModUtil {
5637
5492
  /**
5638
5493
  * Deserializes a list of `SerializedMod`s.
5639
5494
  *
5640
- * @param mods The list of `SerializedMod`s to deserialize.
5495
+ * @param mods The list of `SerializedMod`s to deserialize. If a string is provided, it will be parsed as JSON.
5641
5496
  * @returns The deserialized list of `Mod`s.
5642
5497
  */
5643
5498
  static deserializeMods(mods) {
5499
+ const serializedMods = typeof mods === "string"
5500
+ ? JSON.parse(mods)
5501
+ : mods;
5644
5502
  const map = new ModMap();
5645
- for (const serializedMod of mods) {
5503
+ for (const serializedMod of serializedMods) {
5646
5504
  const modType = this.allMods.get(serializedMod.acronym);
5647
5505
  if (!modType) {
5648
5506
  continue;
@@ -7156,16 +7014,11 @@ class Beatmap {
7156
7014
  * @param mods The modifications to calculate for. Defaults to No Mod.
7157
7015
  */
7158
7016
  maxDroidScore(mods) {
7159
- let scoreMultiplier = 1;
7160
- if (mods) {
7161
- for (const mod of mods.values()) {
7162
- if (mod.isApplicableToDroid()) {
7163
- scoreMultiplier *= mod.calculateDroidScoreMultiplier(this.difficulty);
7164
- }
7165
- }
7166
- if (mods.has(ModScoreV2)) {
7167
- return 1e6 * scoreMultiplier;
7168
- }
7017
+ const scoreMultiplier = mods
7018
+ ? ModUtil.calculateScoreMultiplier(mods.values(), exports.Modes.droid, this)
7019
+ : 1;
7020
+ if (mods === null || mods === void 0 ? void 0 : mods.has(ModScoreV2)) {
7021
+ return 1e6 * scoreMultiplier;
7169
7022
  }
7170
7023
  const difficultyMultiplier = 1 +
7171
7024
  this.difficulty.od / 10 +
@@ -7203,16 +7056,11 @@ class Beatmap {
7203
7056
  maxOsuScore(mods) {
7204
7057
  const accumulatedDiffPoints = this.difficulty.cs + this.difficulty.hp + this.difficulty.od;
7205
7058
  let difficultyMultiplier = 2;
7206
- let scoreMultiplier = 1;
7207
- if (mods) {
7208
- for (const mod of mods.values()) {
7209
- if (mod.isApplicableToOsu()) {
7210
- scoreMultiplier *= mod.osuScoreMultiplier;
7211
- }
7212
- }
7213
- if (mods.has(ModScoreV2)) {
7214
- return 1e6 * scoreMultiplier;
7215
- }
7059
+ const scoreMultiplier = mods
7060
+ ? ModUtil.calculateScoreMultiplier(mods.values(), exports.Modes.osu, this)
7061
+ : 1;
7062
+ if (mods === null || mods === void 0 ? void 0 : mods.has(ModScoreV2)) {
7063
+ return 1e6 * scoreMultiplier;
7216
7064
  }
7217
7065
  switch (true) {
7218
7066
  case accumulatedDiffPoints <= 5:
@@ -11843,7 +11691,7 @@ class ModOldNightCore extends ModNightCore {
11843
11691
  super();
11844
11692
  this.trackRateMultiplier.value = 1.39;
11845
11693
  }
11846
- calculateDroidScoreMultiplier() {
11694
+ get droidScoreMultiplier() {
11847
11695
  return 1.12;
11848
11696
  }
11849
11697
  }