@rian8337/osu-difficulty-calculator 1.0.0

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.
@@ -0,0 +1,1052 @@
1
+ declare module "@rian8337/osu-difficulty-calculator" {
2
+ import { Accuracy, Beatmap, HitObject, MapStats, Mod, modes, OsuHitWindow, Slider, Vector2 } from "@rian8337/osu-base";
3
+
4
+ //#region Classes
5
+
6
+ /**
7
+ * Represents an osu!standard hit object with difficulty calculation values.
8
+ */
9
+ export class DifficultyHitObject {
10
+ /**
11
+ * The underlying hitobject.
12
+ */
13
+ readonly object: HitObject;
14
+ /**
15
+ * The aim strain generated by the hitobject if sliders are considered.
16
+ */
17
+ aimStrainWithSliders: number;
18
+
19
+ /**
20
+ * The aim strain generated by the hitobject if sliders are not considered.
21
+ */
22
+ aimStrainWithoutSliders: number;
23
+ /**
24
+ * The tap strain generated by the hitobject.
25
+ */
26
+ tapStrain: number;
27
+ /**
28
+ * The tap strain generated by the hitobject if `strainTime` isn't modified by
29
+ * OD. This is used in three-finger detection.
30
+ */
31
+ originalTapStrain: number;
32
+ /**
33
+ * The rhythm multiplier generated by the hitobject.
34
+ */
35
+ rhythmMultiplier: number;
36
+ /**
37
+ * The flashlight strain generated by the hitobject.
38
+ */
39
+ flashlightStrain: number;
40
+ /**
41
+ * The normalized distance from the "lazy" end position of the previous hitobject to the start position of this hitobject.
42
+ *
43
+ * The "lazy" end position is the position at which the cursor ends up if the previous hitobject is followed with as minimal movement as possible (i.e. on the edge of slider follow circles).
44
+ */
45
+ lazyJumpDistance: number;
46
+ /**
47
+ * The normalized shortest distance to consider for a jump between the previous hitobject and this hitobject.
48
+ *
49
+ * This is bounded from above by `lazyJumpDistance`, and is smaller than the former if a more natural path is able to be taken through the previous hitobject.
50
+ *
51
+ * Suppose a linear slider - circle pattern. Following the slider lazily (see: `lazyJumpDistance`) will result in underestimating the true end position of the slider as being closer towards the start position.
52
+ * As a result, `lazyJumpDistance` overestimates the jump distance because the player is able to take a more natural path by following through the slider to its end,
53
+ * such that the jump is felt as only starting from the slider's true end position.
54
+ *
55
+ * Now consider a slider - circle pattern where the circle is stacked along the path inside the slider.
56
+ * In this case, the lazy end position correctly estimates the true end position of the slider and provides the more natural movement path.
57
+ */
58
+ minimumJumpDistance: number;
59
+ /**
60
+ * The time taken to travel through `minimumJumpDistance`, with a minimum value of 25ms.
61
+ */
62
+ minimumJumpTime: number;
63
+ /**
64
+ * The normalized distance between the start and end position of this hitobject.
65
+ */
66
+ travelDistance: number;
67
+ /**
68
+ * The time taken to travel through `travelDistance`, with a minimum value of 25ms for a non-zero distance.
69
+ */
70
+ travelTime: number;
71
+ /**
72
+ * Angle the player has to take to hit this hitobject.
73
+ *
74
+ * Calculated as the angle between the circles (current-2, current-1, current).
75
+ */
76
+ angle: number | null;
77
+ /**
78
+ * The amount of milliseconds elapsed between this hitobject and the last hitobject.
79
+ */
80
+ deltaTime: number;
81
+ /**
82
+ * The amount of milliseconds elapsed since the start time of the previous hitobject, with a minimum of 25ms.
83
+ */
84
+ strainTime: number;
85
+ /**
86
+ * Adjusted start time of the hitobject, taking speed multiplier into account.
87
+ */
88
+ startTime: number;
89
+ /**
90
+ * @param object The underlying hitobject.
91
+ */
92
+ constructor(object: HitObject);
93
+ }
94
+
95
+ /**
96
+ * A converter used to convert normal hitobjects into difficulty hitobjects.
97
+ */
98
+ export class DifficultyHitObjectCreator {
99
+ /**
100
+ * The threshold for small circle buff for osu!droid.
101
+ */
102
+ private readonly DROID_CIRCLESIZE_BUFF_THRESHOLD: number;
103
+ /**
104
+ * The threshold for small circle buff for osu!standard.
105
+ */
106
+ private readonly PC_CIRCLESIZE_BUFF_THRESHOLD: number;
107
+ /**
108
+ * The radius of hitobjects.
109
+ */
110
+ private hitObjectRadius: number;
111
+ /**
112
+ * The base normalized radius of hitobjects.
113
+ */
114
+ private readonly normalizedRadius: number;
115
+ /**
116
+ * Generates difficulty hitobjects for difficulty calculation.
117
+ */
118
+ generateDifficultyObjects(params: {
119
+ objects: HitObject[];
120
+ circleSize: number;
121
+ speedMultiplier: number;
122
+ mode: modes;
123
+ }): DifficultyHitObject[];
124
+ /**
125
+ * Calculates a slider's cursor position.
126
+ */
127
+ private calculateSliderCursorPosition(slider: Slider): void;
128
+ /**
129
+ * Gets the scaling factor of a radius.
130
+ *
131
+ * @param mode The mode to get the scaling factor from.
132
+ * @param radius The radiust to get the scaling factor from.
133
+ */
134
+ private getScalingFactor(mode: modes, radius: number): number;
135
+ /**
136
+ * Returns the end cursor position of a hitobject.
137
+ */
138
+ private getEndCursorPosition(object: HitObject): Vector2;
139
+ }
140
+
141
+ /**
142
+ * Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
143
+ */
144
+ export class DroidAim extends DroidSkill {
145
+ protected override readonly skillMultiplier: number;
146
+ protected override readonly strainDecayBase: number;
147
+ protected override readonly reducedSectionBaseline: number;
148
+ protected override readonly reducedSectionCount: number;
149
+ protected override readonly starsPerDouble: number;
150
+ /**
151
+ * Spacing threshold for a single hitobject spacing.
152
+ */
153
+ private readonly SINGLE_SPACING_THRESHOLD: number;
154
+ private readonly minSpeedBonus: number;
155
+ private readonly wideAngleMultiplier: number;
156
+ private readonly acuteAngleMultiplier: number;
157
+ private readonly sliderMultiplier: number;
158
+ private readonly velocityChangeMultiplier: number;
159
+ private readonly withSliders: boolean;
160
+ constructor(mods: Mod[], withSliders: boolean);
161
+ /**
162
+ * @param current The hitobject to calculate.
163
+ */
164
+ protected strainValueOf(current: DifficultyHitObject): number;
165
+ /**
166
+ * @param current The hitobject to calculate.
167
+ */
168
+ protected override strainValueAt(current: DifficultyHitObject): number;
169
+ /**
170
+ * @param currentObject The hitobject to save to.
171
+ */
172
+ protected override saveToHitObject(current: DifficultyHitObject): void;
173
+ }
174
+
175
+ /**
176
+ * Represents the skill required to memorize and hit every object in a beatmap with the Flashlight mod enabled.
177
+ */
178
+ export class DroidFlashlight extends DroidSkill {
179
+ protected override readonly historyLength: number;
180
+ protected override readonly skillMultiplier: number;
181
+ protected override readonly strainDecayBase: number;
182
+ protected override readonly reducedSectionBaseline: number;
183
+ protected override readonly reducedSectionCount: number;
184
+ protected override readonly starsPerDouble: number;
185
+ /**
186
+ * @param current The hitobject to calculate.
187
+ */
188
+ protected strainValueOf(current: DifficultyHitObject): number;
189
+ /**
190
+ * @param current The hitobject to calculate.
191
+ */
192
+ protected override strainValueAt(current: DifficultyHitObject): number;
193
+ /**
194
+ * @param current The hitobject to save to.
195
+ */
196
+ protected override saveToHitObject(current: DifficultyHitObject): void;
197
+ }
198
+ /**
199
+ * A performance points calculator that calculates performance points for osu!droid gamemode.
200
+ */
201
+ export class DroidPerformanceCalculator extends PerformanceCalculator {
202
+ override stars: DroidStarRating;
203
+ protected override finalMultiplier: number;
204
+ /**
205
+ * The aim performance value.
206
+ */
207
+ aim: number;
208
+ /**
209
+ * The tap performance value.
210
+ */
211
+ tap: number;
212
+ /**
213
+ * The accuracy performance value.
214
+ */
215
+ accuracy: number;
216
+ /**
217
+ * The flashlight performance value.
218
+ */
219
+ flashlight: number;
220
+ private averageRhythmMultiplier: number;
221
+ override calculate(params: {
222
+ /**
223
+ * The star rating instance to calculate.
224
+ */
225
+ stars: DroidStarRating;
226
+ /**
227
+ * The maximum combo achieved in the score.
228
+ */
229
+ combo?: number;
230
+ /**
231
+ * The accuracy achieved in the score.
232
+ */
233
+ accPercent?: Accuracy | number;
234
+ /**
235
+ * The amount of misses achieved in the score.
236
+ */
237
+ miss?: number;
238
+ /**
239
+ * The tap penalty to apply for penalized scores.
240
+ */
241
+ tapPenalty?: number;
242
+ /**
243
+ * Custom map statistics to apply custom tap multiplier and force AR values as well as old statistics.
244
+ */
245
+ stats?: MapStats;
246
+ }): this;
247
+ /**
248
+ * Calculates the average rhythm multiplier of the beatmap.
249
+ */
250
+ private calculateAverageRhythmMultiplier(): void;
251
+ /**
252
+ * Calculates the aim performance value of the beatmap.
253
+ */
254
+ private calculateAimValue(): void;
255
+ /**
256
+ * Calculates the tap performance value of the beatmap.
257
+ */
258
+ private calculateTapValue(): void;
259
+ /**
260
+ * Calculates the accuracy performance value of the beatmap.
261
+ */
262
+ private calculateAccuracyValue(): void;
263
+ /**
264
+ * Calculates the flashlight performance value of the beatmap.
265
+ */
266
+ private calculateFlashlightValue(): void;
267
+ override toString(): string;
268
+ }
269
+
270
+ /**
271
+ * Difficulty calculator for osu!droid gamemode.
272
+ */
273
+ export class DroidStarRating extends StarRating {
274
+ /**
275
+ * The aim star rating of the beatmap.
276
+ */
277
+ aim: number;
278
+ /**
279
+ * The tap star rating of the beatmap.
280
+ */
281
+ tap: number;
282
+ /**
283
+ * The flashlight star rating of the beatmap.
284
+ */
285
+ flashlight: number;
286
+ protected override readonly difficultyMultiplier: number;
287
+ /**
288
+ * Calculates the star rating of the specified beatmap.
289
+ *
290
+ * The beatmap is analyzed in chunks of `sectionLength` duration.
291
+ * For each chunk the highest hitobject strains are added to
292
+ * a list which is then collapsed into a weighted sum, much
293
+ * like scores are weighted on a user's profile.
294
+ *
295
+ * For subsequent chunks, the initial max strain is calculated
296
+ * by decaying the previous hitobject's strain until the
297
+ * beginning of the new chunk.
298
+ *
299
+ * The first object doesn't generate a strain
300
+ * so we begin calculating from the second object.
301
+ *
302
+ * Also don't forget to manually add the peak strain for the last
303
+ * section which would otherwise be ignored.
304
+ */
305
+ calculate(params: {
306
+ /**
307
+ * The beatmap to calculate.
308
+ */
309
+ map: Beatmap;
310
+ /**
311
+ * Applied modifications.
312
+ */
313
+ mods?: Mod[];
314
+ /**
315
+ * Custom map statistics to apply custom tap multiplier as well as old statistics.
316
+ */
317
+ stats?: MapStats;
318
+ }): this;
319
+ /**
320
+ * Calculates the aim star rating of the beatmap and stores it in this instance.
321
+ */
322
+ calculateAim(): void;
323
+ /**
324
+ * Calculates the tap star rating of the beatmap and stores it in this instance.
325
+ */
326
+ calculateTap(): void;
327
+ /**
328
+ * Calculates the flashlight star rating of the beatmap and stores it in this instance.
329
+ */
330
+ calculateFlashlight(): void;
331
+ /**
332
+ * Calculates the total star rating of the beatmap and stores it in this instance.
333
+ */
334
+ calculateTotal(): void;
335
+ /**
336
+ * Calculates every star rating of the beatmap and stores it in this instance.
337
+ */
338
+ calculateAll(): void;
339
+ /**
340
+ * Returns a string representative of the class.
341
+ */
342
+ toString(): string;
343
+ /**
344
+ * Creates skills to be calculated.
345
+ */
346
+ protected override createSkills(): DroidSkill[];
347
+ /**
348
+ * Calculates the base rating value of a difficulty.
349
+ */
350
+ private baseRatingValue(difficulty: number): number;
351
+ /**
352
+ * Calculates the base performance value of a difficulty rating.
353
+ *
354
+ * @param rating The difficulty rating.
355
+ */
356
+ private basePerformanceValue(rating: number): number;
357
+ }
358
+
359
+ /**
360
+ * Represents the skill required to press keys or tap with regards to keeping up with the speed at which objects need to be hit.
361
+ */
362
+ export class DroidTap extends DroidSkill {
363
+ protected override readonly historyLength: number;
364
+ protected override readonly skillMultiplier: number;
365
+ protected override readonly strainDecayBase: number;
366
+ protected override readonly reducedSectionBaseline: number;
367
+ protected override readonly reducedSectionCount: number;
368
+ protected override readonly starsPerDouble: number;
369
+ private readonly minSpeedBonus: number;
370
+ private currentTapStrain: number;
371
+ private currentOriginalTapStrain: number;
372
+ private readonly rhythmMultiplier: number;
373
+ private readonly historyTimeMax: number;
374
+ private currentRhythm: number;
375
+ private readonly overallDifficulty: number;
376
+ private readonly hitWindow: OsuHitWindow;
377
+ constructor(mods: Mod[], overallDifficulty: number);
378
+ /**
379
+ * @param current The hitobject to calculate.
380
+ */
381
+ protected strainValueOf(current: DifficultyHitObject): number;
382
+ /**
383
+ * Calculates the tap strain of a hitobject given a specific speed bonus and strain time.
384
+ */
385
+ private tapStrainOf(speedBonus: number, strainTime: number): number;
386
+ /**
387
+ * @param current The hitobject to calculate.
388
+ */
389
+ protected override strainValueAt(current: DifficultyHitObject): number;
390
+ /**
391
+ * @param current The hitobject to save to.
392
+ */
393
+ protected override saveToHitObject(current: DifficultyHitObject): void;
394
+ }
395
+
396
+ /**
397
+ * Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
398
+ */
399
+ export class OsuAim extends OsuSkill {
400
+ protected override readonly skillMultiplier: number;
401
+ protected override readonly strainDecayBase: number;
402
+ protected override readonly reducedSectionCount: number;
403
+ protected override readonly reducedSectionBaseline: number;
404
+ protected override readonly difficultyMultiplier: number;
405
+ protected override readonly decayWeight: number;
406
+ private readonly wideAngleMultiplier: number;
407
+ private readonly acuteAngleMultiplier: number;
408
+ private readonly sliderMultiplier: number;
409
+ private readonly velocityChangeMultiplier: number;
410
+ private readonly withSliders: boolean;
411
+ constructor(mods: Mod[], withSliders: boolean);
412
+ /**
413
+ * @param current The hitobject to calculate.
414
+ */
415
+ protected strainValueOf(current: DifficultyHitObject): number;
416
+ /**
417
+ * @param current The hitobject to calculate.
418
+ */
419
+ protected override strainValueAt(current: DifficultyHitObject): number;
420
+ /**
421
+ * @param current The hitobject to save to.
422
+ */
423
+ protected override saveToHitObject(current: DifficultyHitObject): void;
424
+ }
425
+
426
+ /**
427
+ * Represents the skill required to memorize and hit every object in a beatmap with the Flashlight mod enabled.
428
+ */
429
+ export class OsuFlashlight extends OsuSkill {
430
+ protected override readonly historyLength: number;
431
+ protected override readonly skillMultiplier: number;
432
+ protected override readonly strainDecayBase: number;
433
+ protected override readonly reducedSectionCount: number;
434
+ protected override readonly reducedSectionBaseline: number;
435
+ protected override readonly difficultyMultiplier: number;
436
+ protected override readonly decayWeight: number;
437
+ /**
438
+ * @param current The hitobject to calculate.
439
+ */
440
+ protected strainValueOf(current: DifficultyHitObject): number;
441
+ /**
442
+ * @param current The hitobject to calculate.
443
+ */
444
+ protected override strainValueAt(current: DifficultyHitObject): number;
445
+ /**
446
+ * @param current The hitobject to save to.
447
+ */
448
+ protected override saveToHitObject(current: DifficultyHitObject): void;
449
+ }
450
+
451
+ /**
452
+ * A performance points calculator that calculates performance points for osu!standard gamemode.
453
+ */
454
+ export class OsuPerformanceCalculator extends PerformanceCalculator {
455
+ override stars: OsuStarRating;
456
+ protected override finalMultiplier: number;
457
+ /**
458
+ * The aim performance value.
459
+ */
460
+ aim: number;
461
+ /**
462
+ * The speed performance value.
463
+ */
464
+ speed: number;
465
+ /**
466
+ * The accuracy performance value.
467
+ */
468
+ accuracy: number;
469
+ /**
470
+ * The flashlight performance value.
471
+ */
472
+ flashlight: number;
473
+ override calculate(params: {
474
+ /**
475
+ * The star rating instance to calculate.
476
+ */
477
+ stars: OsuStarRating;
478
+ /**
479
+ * The maximum combo achieved in the score.
480
+ */
481
+ combo?: number;
482
+ /**
483
+ * The accuracy achieved in the score.
484
+ */
485
+ accPercent?: Accuracy | number;
486
+ /**
487
+ * The amount of misses achieved in the score.
488
+ */
489
+ miss?: number;
490
+ /**
491
+ * Custom map statistics to apply custom speed multiplier and force AR values as well as old statistics.
492
+ */
493
+ stats?: MapStats;
494
+ }): this;
495
+ /**
496
+ * Calculates the aim performance value of the beatmap.
497
+ */
498
+ private calculateAimValue(): void;
499
+ /**
500
+ * Calculates the speed performance value of the beatmap.
501
+ */
502
+ private calculateSpeedValue(): void;
503
+ /**
504
+ * Calculates the accuracy performance value of the beatmap.
505
+ */
506
+ private calculateAccuracyValue(): void;
507
+ /**
508
+ * Calculates the flashlight performance value of the beatmap.
509
+ */
510
+ private calculateFlashlightValue(): void;
511
+ override toString(): string;
512
+ }
513
+
514
+ /**
515
+ * Represents the skill required to press keys or tap with regards to keeping up with the speed at which objects need to be hit.
516
+ */
517
+ export class OsuSpeed extends OsuSkill {
518
+ /**
519
+ * Spacing threshold for a single hitobject spacing.
520
+ */
521
+ private readonly SINGLE_SPACING_THRESHOLD: number;
522
+ private readonly angleBonusBegin: number;
523
+ protected override readonly skillMultiplier: number;
524
+ protected override readonly strainDecayBase: number;
525
+ protected override readonly reducedSectionCount: number;
526
+ protected override readonly reducedSectionBaseline: number;
527
+ protected override readonly difficultyMultiplier: number;
528
+ protected override readonly decayWeight: number;
529
+ private readonly rhythmMultiplier: number;
530
+ private readonly historyTimeMax: number;
531
+ private currentSpeedStrain: number;
532
+ private currentRhythm: number;
533
+ private readonly minSpeedBonus: number;
534
+ private readonly greatWindow: number;
535
+ constructor(mods: Mod[], greatWindow: number);
536
+ /**
537
+ * @param current The hitobject to calculate.
538
+ */
539
+ protected strainValueOf(current: DifficultyHitObject): number;
540
+ /**
541
+ * @param current The hitobject to calculate.
542
+ */
543
+ protected override strainValueAt(current: DifficultyHitObject): number;
544
+ /**
545
+ * Calculates a rhythm multiplier for the difficulty of the tap associated with historic data of the current object.
546
+ */
547
+ private calculateRhythmBonus(current: DifficultyHitObject): number;
548
+ /**
549
+ * @param current The hitobject to save to.
550
+ */
551
+ protected override saveToHitObject(current: DifficultyHitObject): void;
552
+ }
553
+
554
+ /**
555
+ * Difficulty calculator for osu!standard gamemode.
556
+ */
557
+ export class OsuStarRating extends StarRating {
558
+ /**
559
+ * The aim star rating of the beatmap.
560
+ */
561
+ aim: number;
562
+ /**
563
+ * The speed star rating of the beatmap.
564
+ */
565
+ speed: number;
566
+ /**
567
+ * The flashlight star rating of the beatmap.
568
+ */
569
+ flashlight: number;
570
+ protected readonly difficultyMultiplier: number;
571
+ calculate(params: {
572
+ /**
573
+ * The beatmap to calculate.
574
+ */
575
+ map: Beatmap;
576
+ /**
577
+ * Applied modifications.
578
+ */
579
+ mods?: Mod[];
580
+ /**
581
+ * Custom map statistics to apply custom speed multiplier as well as old statistics.
582
+ */
583
+ stats?: MapStats;
584
+ }): this;
585
+ /**
586
+ * Calculates the aim star rating of the beatmap and stores it in this instance.
587
+ */
588
+ calculateAim(): void;
589
+ /**
590
+ * Calculates the speed star rating of the beatmap and stores it in this instance.
591
+ */
592
+ calculateSpeed(): void;
593
+ /**
594
+ * Calculates the flashlight star rating of the beatmap and stores it in this instance.
595
+ */
596
+ calculateFlashlight(): void;
597
+ /**
598
+ * Calculates the total star rating of the beatmap and stores it in this instance.
599
+ */
600
+ calculateTotal(): void;
601
+ /**
602
+ * Calculates every star rating of the beatmap and stores it in this instance.
603
+ */
604
+ calculateAll(): void;
605
+ /**
606
+ * Returns a string representative of the class.
607
+ */
608
+ toString(): string;
609
+ /**
610
+ * Creates skills to be calculated.
611
+ */
612
+ protected override createSkills(): OsuSkill[];
613
+ /**
614
+ * Calculates the base performance value of a difficulty rating.
615
+ *
616
+ * @param rating The difficulty rating.
617
+ */
618
+ private basePerformanceValue(rating: number): number;
619
+ /**
620
+ * Calculates the star rating value of a difficulty.
621
+ *
622
+ * @param difficulty The difficulty to calculate.
623
+ */
624
+ private starValue(difficulty: number): number;
625
+ }
626
+
627
+ //#endregion
628
+
629
+ //#region Interfaces
630
+
631
+ /**
632
+ * Holds additional data that is used in difficulty calculation.
633
+ */
634
+ export interface DifficultyAttributes {
635
+ speedNoteCount: number;
636
+ sliderFactor: number;
637
+ }
638
+
639
+ /**
640
+ * The strain peaks of various calculated difficulties.
641
+ */
642
+ export interface StrainPeaks {
643
+ /**
644
+ * The strain peaks of aim difficulty if sliders are considered.
645
+ */
646
+ aimWithSliders: number[];
647
+ /**
648
+ * The strain peaks of aim difficulty if sliders are not considered.
649
+ */
650
+ aimWithoutSliders: number[];
651
+ /**
652
+ * The strain peaks of speed difficulty.
653
+ */
654
+ speed: number[];
655
+ /**
656
+ * The strain peaks of flashlight difficulty.
657
+ */
658
+ flashlight: number[];
659
+ }
660
+
661
+ //#endregion
662
+
663
+ //#region Unexported classes
664
+
665
+ /**
666
+ * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
667
+ * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
668
+ */
669
+ abstract class DroidSkill extends StrainSkill {
670
+ /**
671
+ * The bonus multiplier that is given for a sequence of notes of equal difficulty.
672
+ */
673
+ protected abstract readonly starsPerDouble: number;
674
+ override difficultyValue(): number;
675
+ }
676
+
677
+ /**
678
+ * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
679
+ * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
680
+ */
681
+ abstract class OsuSkill extends StrainSkill {
682
+ /**
683
+ * The number of sections with the highest strains, which the peak strain reductions will apply to.
684
+ * This is done in order to decrease their impact on the overall difficulty of the map for this skill.
685
+ */
686
+ protected abstract readonly reducedSectionCount: number;
687
+ /**
688
+ * The baseline multiplier applied to the section with the biggest strain.
689
+ */
690
+ protected abstract readonly reducedSectionBaseline: number;
691
+ /**
692
+ * The final multiplier to be applied to the final difficulty value after all other calculations.
693
+ */
694
+ protected abstract readonly difficultyMultiplier: number;
695
+ /**
696
+ * The weight by which each strain value decays.
697
+ */
698
+ protected abstract readonly decayWeight: number;
699
+ override difficultyValue(): number;
700
+ }
701
+
702
+ /**
703
+ * The base class of performance calculators.
704
+ */
705
+ abstract class PerformanceCalculator {
706
+ /**
707
+ * The overall performance value.
708
+ */
709
+ total: number;
710
+ /**
711
+ * The calculated accuracy.
712
+ */
713
+ computedAccuracy: Accuracy;
714
+ /**
715
+ * Bitwise value of enabled modifications.
716
+ */
717
+ protected convertedMods: number;
718
+ /**
719
+ * The calculated beatmap.
720
+ */
721
+ abstract stars: StarRating;
722
+ /**
723
+ * The map statistics after applying modifications.
724
+ */
725
+ protected mapStatistics: MapStats;
726
+ /**
727
+ * Penalty for combo breaks.
728
+ */
729
+ protected comboPenalty: number;
730
+ /**
731
+ * The global multiplier to be applied to the final performance value.
732
+ *
733
+ * This is being adjusted to keep the final value scaled around what it used to be when changing things.
734
+ */
735
+ protected abstract finalMultiplier: number;
736
+ /**
737
+ * The amount of misses that are filtered out from sliderbreaks.
738
+ */
739
+ protected effectiveMissCount: number;
740
+
741
+ /**
742
+ * Nerf factor used for nerfing beatmaps with very likely dropped sliderends.
743
+ */
744
+ protected sliderNerfFactor: number;
745
+ /**
746
+ * Calculates the performance points of a beatmap.
747
+ */
748
+ abstract calculate(params: {
749
+ /**
750
+ * The star rating instance to calculate.
751
+ */
752
+ stars: StarRating;
753
+ /**
754
+ * The maximum combo achieved in the score.
755
+ */
756
+ combo?: number;
757
+ /**
758
+ * The accuracy achieved in the score.
759
+ */
760
+ accPercent?: Accuracy | number;
761
+ /**
762
+ * The amount of misses achieved in the score.
763
+ */
764
+ miss?: number;
765
+ /**
766
+ * The gamemode to calculate.
767
+ */
768
+ mode?: modes;
769
+ /**
770
+ * The speed penalty to apply for penalized scores. Only applies to droid gamemode.
771
+ */
772
+ speedPenalty?: number;
773
+ /**
774
+ * Custom map statistics to apply custom speed multiplier and force AR values as well as old statistics.
775
+ */
776
+ stats?: MapStats;
777
+ }): this;
778
+ /**
779
+ * Returns a string representative of the class.
780
+ */
781
+ abstract toString(): string;
782
+ /**
783
+ * Calculates the base performance value for of a star rating.
784
+ */
785
+ protected baseValue(stars: number): number;
786
+ /**
787
+ * Processes given parameters for usage in performance calculation.
788
+ */
789
+ protected handleParams(
790
+ params: {
791
+ /**
792
+ * The star rating instance to calculate.
793
+ */
794
+ stars: StarRating;
795
+ /**
796
+ * The maximum combo achieved in the score.
797
+ */
798
+ combo?: number;
799
+ /**
800
+ * The accuracy achieved in the score.
801
+ */
802
+ accPercent?: Accuracy | number;
803
+ /**
804
+ * The amount of misses achieved in the score.
805
+ */
806
+ miss?: number;
807
+ /**
808
+ * The gamemode to calculate.
809
+ */
810
+ mode?: modes;
811
+ /**
812
+ * The speed penalty to apply for penalized scores.
813
+ */
814
+ speedPenalty?: number;
815
+ /**
816
+ * Custom map statistics to apply custom speed multiplier and force AR values as well as old statistics.
817
+ */
818
+ stats?: MapStats;
819
+ },
820
+ mode: modes
821
+ ): void;
822
+ /**
823
+ * Calculates the amount of misses + sliderbreaks from combo.
824
+ */
825
+ private calculateEffectiveMissCount(combo: number, maxCombo: number): number;
826
+ }
827
+
828
+ /**
829
+ * A bare minimal abstract skill for fully custom skill implementations.
830
+ */
831
+ abstract class Skill {
832
+ /**
833
+ * The hitobjects that were processed previously. They can affect the strain values of the following objects.
834
+ *
835
+ * The latest hitobject is at index 0.
836
+ */
837
+ protected readonly previous: DifficultyHitObject[];
838
+ /**
839
+ * Number of previous hitobjects to keep inside the `previous` array.
840
+ */
841
+ protected readonly historyLength: number;
842
+ /**
843
+ * The mods that this skill processes.
844
+ */
845
+ protected readonly mods: Mod[];
846
+ processInternal(current: DifficultyHitObject): void;
847
+ /**
848
+ * Processes a hitobject.
849
+ *
850
+ * @param current The hitobject to process.
851
+ */
852
+ protected abstract process(current: DifficultyHitObject): void;
853
+ /**
854
+ * Returns the calculated difficulty value representing all hitobjects that have been processed up to this point.
855
+ */
856
+ abstract difficultyValue(): number;
857
+ }
858
+
859
+ /**
860
+ * The base of difficulty calculation.
861
+ */
862
+ abstract class StarRating {
863
+ /**
864
+ * The calculated beatmap.
865
+ */
866
+ map: Beatmap;
867
+ /**
868
+ * The difficulty objects of the beatmap.
869
+ */
870
+ readonly objects: DifficultyHitObject[];
871
+ /**
872
+ * The modifications applied.
873
+ */
874
+ mods: Mod[];
875
+ /**
876
+ * The total star rating of the beatmap.
877
+ */
878
+ total: number;
879
+ /**
880
+ * The map statistics of the beatmap after modifications are applied.
881
+ */
882
+ stats: MapStats;
883
+ /**
884
+ * The strain peaks of various calculated difficulties.
885
+ */
886
+ readonly strainPeaks: StrainPeaks;
887
+ /**
888
+ * Additional data that is used in performance calculation.
889
+ */
890
+ readonly attributes: DifficultyAttributes;
891
+ protected readonly sectionLength: number;
892
+ protected abstract readonly difficultyMultiplier: number;
893
+ /**
894
+ * Calculates the star rating of the specified beatmap.
895
+ *
896
+ * The beatmap is analyzed in chunks of `sectionLength` duration.
897
+ * For each chunk the highest hitobject strains are added to
898
+ * a list which is then collapsed into a weighted sum, much
899
+ * like scores are weighted on a user's profile.
900
+ *
901
+ * For subsequent chunks, the initial max strain is calculated
902
+ * by decaying the previous hitobject's strain until the
903
+ * beginning of the new chunk.
904
+ *
905
+ * The first object doesn't generate a strain
906
+ * so we begin calculating from the second object.
907
+ *
908
+ * Also don't forget to manually add the peak strain for the last
909
+ * section which would otherwise be ignored.
910
+ */
911
+ protected calculate(
912
+ params: {
913
+ /**
914
+ * The beatmap to calculate.
915
+ */
916
+ map: Beatmap;
917
+ /**
918
+ * Applied modifications.
919
+ */
920
+ mods?: Mod[];
921
+ /**
922
+ * Custom map statistics to apply custom speed multiplier as well as old statistics.
923
+ */
924
+ stats?: MapStats;
925
+ },
926
+ mode: modes
927
+ ): this;
928
+ /**
929
+ * Generates difficulty hitobjects for this calculator.
930
+ *
931
+ * @param mode The gamemode to generate difficulty hitobjects for.
932
+ */
933
+ generateDifficultyHitObjects(mode: modes): void;
934
+ /**
935
+ * Calculates the skills provided.
936
+ *
937
+ * @param skills The skills to calculate.
938
+ */
939
+ protected calculateSkills(...skills: Skill[]): void;
940
+ /**
941
+ * Calculates the total star rating of the beatmap and stores it in this instance.
942
+ */
943
+ abstract calculateTotal(): void;
944
+ /**
945
+ * Calculates every star rating of the beatmap and stores it in this instance.
946
+ */
947
+ abstract calculateAll(): void;
948
+ /**
949
+ * Generates the strain chart of this beatmap and returns the chart as a buffer.
950
+ *
951
+ * @param beatmapsetID The beatmapset ID to get background image from. If omitted, the background will be plain white.
952
+ * @param color The color of the graph.
953
+ */
954
+ getStrainChart(
955
+ beatmapsetID?: number,
956
+ color?: string
957
+ ): Promise<Buffer | null>;
958
+ /**
959
+ * Returns a string representative of the class.
960
+ */
961
+ abstract toString(): string;
962
+ /**
963
+ * Creates skills to be calculated.
964
+ */
965
+ protected abstract createSkills(): Skill[];
966
+ }
967
+
968
+ /**
969
+ * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
970
+ * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
971
+ */
972
+ abstract class StrainSkill extends Skill {
973
+ /**
974
+ * The strain of currently calculated hitobject.
975
+ */
976
+ protected currentStrain: number;
977
+
978
+ /**
979
+ * The current section's strain peak.
980
+ */
981
+ protected currentSectionPeak: number;
982
+
983
+ /**
984
+ * Strain peaks are stored here.
985
+ */
986
+ readonly strainPeaks: number[];
987
+ /**
988
+ * The number of sections with the highest strains, which the peak strain reductions will apply to.
989
+ * This is done in order to decrease their impact on the overall difficulty of the map for this skill.
990
+ */
991
+ protected abstract readonly reducedSectionCount: number;
992
+
993
+ /**
994
+ * The baseline multiplier applied to the section with the biggest strain.
995
+ */
996
+ protected abstract readonly reducedSectionBaseline: number;
997
+
998
+ /**
999
+ * Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other.
1000
+ */
1001
+ protected abstract readonly skillMultiplier: number;
1002
+
1003
+ /**
1004
+ * Determines how quickly strain decays for the given skill.
1005
+ *
1006
+ * For example, a value of 0.15 indicates that strain decays to 15% of its original value in one second.
1007
+ */
1008
+ protected abstract readonly strainDecayBase: number;
1009
+
1010
+ protected readonly sectionLength: number;
1011
+
1012
+ protected currentSectionEnd: number;
1013
+
1014
+ /**
1015
+ * Calculates the strain value of a hitobject and stores the value in it. This value is affected by previously processed objects.
1016
+ *
1017
+ * @param current The hitobject to process.
1018
+ */
1019
+ protected override process(current: DifficultyHitObject): void;
1020
+
1021
+ /**
1022
+ * Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty.
1023
+ */
1024
+ saveCurrentPeak(): void;
1025
+
1026
+ /**
1027
+ * Sets the initial strain level for a new section.
1028
+ *
1029
+ * @param offset The beginning of the new section in milliseconds, adjusted by speed multiplier.
1030
+ */
1031
+ protected startNewSectionFrom(offset: number): void;
1032
+
1033
+ /**
1034
+ * Calculates strain decay for a specified time frame.
1035
+ *
1036
+ * @param ms The time frame to calculate.
1037
+ */
1038
+ protected strainDecay(ms: number): number;
1039
+
1040
+ /**
1041
+ * Calculates the strain value at a hitobject.
1042
+ */
1043
+ protected abstract strainValueAt(current: DifficultyHitObject): number;
1044
+
1045
+ /**
1046
+ * Saves the current strain to a hitobject.
1047
+ */
1048
+ protected abstract saveToHitObject(current: DifficultyHitObject): void;
1049
+ }
1050
+
1051
+ //#endregion
1052
+ }