@rian8337/osu-difficulty-calculator 4.0.0-beta.4 → 4.0.0-beta.40

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.
@@ -1,4 +1,4 @@
1
- import { MapStats, Mod, PlaceableHitObject, Modes, Beatmap, HitObject, Accuracy } from '@rian8337/osu-base';
1
+ import { Mod, PlaceableHitObject, Modes, Beatmap, Accuracy } from '@rian8337/osu-base';
2
2
 
3
3
  /**
4
4
  * An evaluator for calculating aim skill.
@@ -20,16 +20,6 @@ declare abstract class AimEvaluator {
20
20
  protected static calculateAcuteAngleBonus(angle: number): number;
21
21
  }
22
22
 
23
- /**
24
- * The base of calculation options.
25
- */
26
- interface CalculationOptions {
27
- /**
28
- * Custom map statistics to apply custom speed multiplier as well as old statistics.
29
- */
30
- stats?: MapStats;
31
- }
32
-
33
23
  /**
34
24
  * Holds data that can be used to calculate performance points.
35
25
  */
@@ -69,11 +59,9 @@ interface DifficultyAttributes {
69
59
  */
70
60
  sliderFactor: number;
71
61
  /**
72
- * The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
73
- *
74
- * Rate-adjusting mods don't directly affect the approach rate difficulty value, but have a perceived effect as a result of adjusting audio timing.
62
+ * The overall clock rate that was applied to the beatmap.
75
63
  */
76
- approachRate: number;
64
+ clockRate: number;
77
65
  /**
78
66
  * The perceived overall difficulty inclusive of rate-adjusting mods (DT/HT/etc), based on osu!standard judgement.
79
67
  *
@@ -92,22 +80,40 @@ interface DifficultyAttributes {
92
80
  * The number of spinners in the beatmap.
93
81
  */
94
82
  spinnerCount: number;
83
+ /**
84
+ * The amount of strains that are considered difficult with respect to the aim skill.
85
+ */
86
+ aimDifficultStrainCount: number;
95
87
  }
96
88
 
89
+ /**
90
+ * Represents difficulty attributes that can be cached.
91
+ */
92
+ type CacheableDifficultyAttributes<T extends DifficultyAttributes> = Omit<T, "mods"> & {
93
+ /**
94
+ * The mods which were applied to the beatmap.
95
+ */
96
+ mods: string;
97
+ };
98
+
97
99
  /**
98
100
  * Represents options for difficulty calculation.
99
101
  */
100
- interface DifficultyCalculationOptions extends CalculationOptions {
102
+ interface DifficultyCalculationOptions {
101
103
  /**
102
104
  * The modifications to apply.
103
105
  */
104
- mods?: Mod[];
106
+ readonly mods?: Mod[];
107
+ /**
108
+ * The custom speed multiplier to apply. Defaults to 1.
109
+ */
110
+ readonly customSpeedMultiplier?: number;
105
111
  }
106
112
 
107
113
  /**
108
- * Represents an osu!standard hit object with difficulty calculation values.
114
+ * Represents a hit object with difficulty calculation values.
109
115
  */
110
- declare class DifficultyHitObject {
116
+ declare abstract class DifficultyHitObject {
111
117
  /**
112
118
  * The underlying hitobject.
113
119
  */
@@ -117,19 +123,7 @@ declare class DifficultyHitObject {
117
123
  *
118
124
  * This is one less than the actual index of the hitobject in the beatmap.
119
125
  */
120
- index: number;
121
- /**
122
- * The preempt time of the hitobject.
123
- */
124
- baseTimePreempt: number;
125
- /**
126
- * Adjusted preempt time of the hitobject, taking speed multiplier into account.
127
- */
128
- timePreempt: number;
129
- /**
130
- * The fade in time of the hitobject.
131
- */
132
- timeFadeIn: number;
126
+ readonly index: number;
133
127
  /**
134
128
  * The aim strain generated by the hitobject if sliders are considered.
135
129
  */
@@ -138,41 +132,10 @@ declare class DifficultyHitObject {
138
132
  * The aim strain generated by the hitobject if sliders are not considered.
139
133
  */
140
134
  aimStrainWithoutSliders: number;
141
- /**
142
- * The tap strain generated by the hitobject.
143
- *
144
- * This is also used for osu!standard as opposed to "speed strain".
145
- */
146
- tapStrain: number;
147
- /**
148
- * The tap strain generated by the hitobject if `strainTime` isn't modified by
149
- * OD. This is used in three-finger detection.
150
- */
151
- originalTapStrain: number;
152
135
  /**
153
136
  * The rhythm multiplier generated by the hitobject. This is used to alter tap strain.
154
137
  */
155
138
  rhythmMultiplier: number;
156
- /**
157
- * The rhythm strain generated by the hitobject.
158
- */
159
- rhythmStrain: number;
160
- /**
161
- * The flashlight strain generated by the hitobject if sliders are considered.
162
- */
163
- flashlightStrainWithSliders: number;
164
- /**
165
- * The flashlight strain generated by the hitobject if sliders are not considered.
166
- */
167
- flashlightStrainWithoutSliders: number;
168
- /**
169
- * The visual strain generated by the hitobject if sliders are considered.
170
- */
171
- visualStrainWithSliders: number;
172
- /**
173
- * The visual strain generated by the hitobject if sliders are not considered.
174
- */
175
- visualStrainWithoutSliders: number;
176
139
  /**
177
140
  * The normalized distance from the "lazy" end position of the previous hitobject to the start position of this hitobject.
178
141
  *
@@ -213,42 +176,55 @@ declare class DifficultyHitObject {
213
176
  /**
214
177
  * The amount of milliseconds elapsed between this hitobject and the last hitobject.
215
178
  */
216
- deltaTime: number;
179
+ readonly deltaTime: number;
217
180
  /**
218
181
  * The amount of milliseconds elapsed since the start time of the previous hitobject, with a minimum of 25ms.
219
182
  */
220
- strainTime: number;
183
+ readonly strainTime: number;
221
184
  /**
222
185
  * Adjusted start time of the hitobject, taking speed multiplier into account.
223
186
  */
224
- startTime: number;
187
+ readonly startTime: number;
225
188
  /**
226
189
  * Adjusted end time of the hitobject, taking speed multiplier into account.
227
190
  */
228
- endTime: number;
191
+ readonly endTime: number;
229
192
  /**
230
- * The note density of the hitobject.
193
+ * The full great window of the hitobject.
231
194
  */
232
- noteDensity: number;
195
+ readonly fullGreatWindow: number;
233
196
  /**
234
- * The overlapping factor of the hitobject.
235
- *
236
- * This is used to scale visual skill.
197
+ * Other hitobjects in the beatmap, including this hitobject.
237
198
  */
238
- overlappingFactor: number;
199
+ protected readonly hitObjects: readonly DifficultyHitObject[];
200
+ protected abstract readonly mode: Modes;
201
+ protected readonly normalizedRadius = 50;
202
+ protected readonly maximumSliderRadius: number;
203
+ protected readonly assumedSliderRadius: number;
239
204
  /**
240
- * Adjusted velocity of the hitobject, taking speed multiplier into account.
205
+ * The lowest possible delta time value.
241
206
  */
242
- velocity: number;
207
+ static readonly minDeltaTime = 25;
208
+ private readonly lastObject;
209
+ private readonly lastLastObject;
243
210
  /**
244
- * Other hitobjects in the beatmap, including this hitobject.
211
+ * Note: You **must** call `computeProperties` at some point due to how TypeScript handles
212
+ * overridden properties (see [this](https://github.com/microsoft/TypeScript/issues/1617) GitHub issue).
213
+ *
214
+ * @param object The underlying hitobject.
215
+ * @param lastObject The hitobject before this hitobject.
216
+ * @param lastLastObject The hitobject before the last hitobject.
217
+ * @param difficultyHitObjects All difficulty hitobjects in the processed beatmap.
218
+ * @param clockRate The clock rate of the beatmap.
245
219
  */
246
- private readonly hitObjects;
220
+ constructor(object: PlaceableHitObject, lastObject: PlaceableHitObject | null, lastLastObject: PlaceableHitObject | null, difficultyHitObjects: readonly DifficultyHitObject[], clockRate: number);
247
221
  /**
248
- * @param object The underlying hitobject.
249
- * @param hitObjects All difficulty hitobjects in the processed beatmap.
222
+ * Computes the properties of this hitobject.
223
+ *
224
+ * @param clockRate The clock rate of the beatmap.
225
+ * @param hitObjects The hitobjects in the beatmap.
250
226
  */
251
- constructor(object: PlaceableHitObject, hitObjects: DifficultyHitObject[]);
227
+ computeProperties(clockRate: number, hitObjects: readonly PlaceableHitObject[]): void;
252
228
  /**
253
229
  * Gets the difficulty hitobject at a specific index with respect to the current
254
230
  * difficulty hitobject's index.
@@ -259,7 +235,7 @@ declare class DifficultyHitObject {
259
235
  * @returns The difficulty hitobject at the index with respect to the current
260
236
  * difficulty hitobject's index, `null` if the index is out of range.
261
237
  */
262
- previous(backwardsIndex: number): DifficultyHitObject | null;
238
+ previous(backwardsIndex: number): this | null;
263
239
  /**
264
240
  * Gets the difficulty hitobject at a specific index with respect to the current
265
241
  * difficulty hitobject's index.
@@ -270,26 +246,48 @@ declare class DifficultyHitObject {
270
246
  * @returns The difficulty hitobject at the index with respect to the current
271
247
  * difficulty hitobject's index, `null` if the index is out of range.
272
248
  */
273
- next(forwardsIndex: number): DifficultyHitObject | null;
249
+ next(forwardsIndex: number): this | null;
274
250
  /**
275
251
  * Calculates the opacity of the hitobject at a given time.
276
252
  *
277
253
  * @param time The time to calculate the hitobject's opacity at.
278
254
  * @param isHidden Whether Hidden mod is used.
279
- * @param mode The gamemode to calculate the opacity for.
280
255
  * @returns The opacity of the hitobject at the given time.
281
256
  */
282
- opacityAt(time: number, isHidden: boolean, mode: Modes): number;
257
+ opacityAt(time: number, isHidden: boolean): number;
283
258
  /**
284
- * Determines whether this hitobject is considered overlapping with the hitobject before it.
259
+ * How possible is it to doubletap this object together with the next one and get perfect
260
+ * judgement in range from 0 to 1.
285
261
  *
286
- * Keep in mind that "overlapping" in this case is overlapping to the point where both hitobjects
287
- * can be hit with just a single tap in osu!droid.
288
- *
289
- * @param considerDistance Whether to consider the distance between both hitobjects.
290
- * @returns Whether the hitobject is considered overlapping.
262
+ * A value closer to 1 indicates a higher possibility.
291
263
  */
292
- isOverlapping(considerDistance: boolean): boolean;
264
+ get doubletapness(): number;
265
+ protected abstract get scalingFactor(): number;
266
+ protected setDistances(clockRate: number): void;
267
+ private calculateSliderCursorPosition;
268
+ private getEndCursorPosition;
269
+ }
270
+
271
+ /**
272
+ * Represents the strain peaks of various calculated difficulties.
273
+ */
274
+ interface StrainPeaks {
275
+ /**
276
+ * The strain peaks of aim difficulty if sliders are considered.
277
+ */
278
+ aimWithSliders: number[];
279
+ /**
280
+ * The strain peaks of aim difficulty if sliders are not considered.
281
+ */
282
+ aimWithoutSliders: number[];
283
+ /**
284
+ * The strain peaks of speed difficulty.
285
+ */
286
+ speed: number[];
287
+ /**
288
+ * The strain peaks of flashlight difficulty.
289
+ */
290
+ flashlight: number[];
293
291
  }
294
292
 
295
293
  /**
@@ -316,103 +314,9 @@ declare abstract class Skill {
316
314
  }
317
315
 
318
316
  /**
319
- * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
320
- * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
321
- */
322
- declare abstract class StrainSkill extends Skill {
323
- /**
324
- * The strain of currently calculated hitobject.
325
- */
326
- protected currentStrain: number;
327
- /**
328
- * The current section's strain peak.
329
- */
330
- protected currentSectionPeak: number;
331
- /**
332
- * Strain peaks are stored here.
333
- */
334
- readonly strainPeaks: number[];
335
- /**
336
- * The number of sections with the highest strains, which the peak strain reductions will apply to.
337
- * This is done in order to decrease their impact on the overall difficulty of the map for this skill.
338
- */
339
- protected abstract readonly reducedSectionCount: number;
340
- /**
341
- * The baseline multiplier applied to the section with the biggest strain.
342
- */
343
- protected abstract readonly reducedSectionBaseline: number;
344
- /**
345
- * Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other.
346
- */
347
- protected abstract readonly skillMultiplier: number;
348
- /**
349
- * Determines how quickly strain decays for the given skill.
350
- *
351
- * For example, a value of 0.15 indicates that strain decays to 15% of its original value in one second.
352
- */
353
- protected abstract readonly strainDecayBase: number;
354
- private readonly sectionLength;
355
- private currentSectionEnd;
356
- private isFirstObject;
357
- /**
358
- * Calculates the strain value of a hitobject and stores the value in it. This value is affected by previously processed objects.
359
- *
360
- * @param current The hitobject to process.
361
- */
362
- process(current: DifficultyHitObject): void;
363
- /**
364
- * Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty.
365
- */
366
- saveCurrentPeak(): void;
367
- /**
368
- * Calculates strain decay for a specified time frame.
369
- *
370
- * @param ms The time frame to calculate.
371
- */
372
- protected strainDecay(ms: number): number;
373
- /**
374
- * Calculates the strain value at a hitobject.
375
- */
376
- protected abstract strainValueAt(current: DifficultyHitObject): number;
377
- /**
378
- * Saves the current strain to a hitobject.
379
- */
380
- protected abstract saveToHitObject(current: DifficultyHitObject): void;
381
- /**
382
- * Sets the initial strain level for a new section.
383
- *
384
- * @param offset The beginning of the new section in milliseconds, adjusted by speed multiplier.
385
- * @param current The current hitobject.
386
- */
387
- private startNewSectionFrom;
388
- }
389
-
390
- /**
391
- * Represents the strain peaks of various calculated difficulties.
317
+ * The base of a difficulty calculator.
392
318
  */
393
- interface StrainPeaks {
394
- /**
395
- * The strain peaks of aim difficulty if sliders are considered.
396
- */
397
- aimWithSliders: number[];
398
- /**
399
- * The strain peaks of aim difficulty if sliders are not considered.
400
- */
401
- aimWithoutSliders: number[];
402
- /**
403
- * The strain peaks of speed difficulty.
404
- */
405
- speed: number[];
406
- /**
407
- * The strain peaks of flashlight difficulty.
408
- */
409
- flashlight: number[];
410
- }
411
-
412
- /**
413
- * The base of difficulty calculators.
414
- */
415
- declare abstract class DifficultyCalculator {
319
+ declare abstract class DifficultyCalculator<THitObject extends DifficultyHitObject, TAttributes extends DifficultyAttributes> {
416
320
  /**
417
321
  * The calculated beatmap.
418
322
  */
@@ -420,7 +324,11 @@ declare abstract class DifficultyCalculator {
420
324
  /**
421
325
  * The difficulty objects of the beatmap.
422
326
  */
423
- readonly objects: DifficultyHitObject[];
327
+ private _objects;
328
+ /**
329
+ * The difficulty objects of the beatmap.
330
+ */
331
+ get objects(): readonly THitObject[];
424
332
  /**
425
333
  * The modifications applied.
426
334
  */
@@ -428,26 +336,25 @@ declare abstract class DifficultyCalculator {
428
336
  /**
429
337
  * The total star rating of the beatmap.
430
338
  */
431
- total: number;
432
- /**
433
- * The map statistics of the beatmap after modifications are applied.
434
- */
435
- stats: MapStats;
339
+ get total(): number;
436
340
  /**
437
341
  * The strain peaks of various calculated difficulties.
438
342
  */
439
343
  readonly strainPeaks: StrainPeaks;
440
344
  /**
441
- * Holds data that can be used to calculate performance points.
345
+ * The difficulty attributes that can be used to calculate performance points.
442
346
  */
443
- abstract readonly attributes: DifficultyAttributes;
444
- protected readonly sectionLength: number;
347
+ abstract readonly attributes: TAttributes;
348
+ /**
349
+ * The difficulty attributes that can be cached. It can also be used to calculate performance points.
350
+ */
351
+ abstract get cacheableAttributes(): CacheableDifficultyAttributes<TAttributes>;
445
352
  protected abstract readonly difficultyMultiplier: number;
446
353
  protected abstract readonly mode: Modes;
447
354
  /**
448
355
  * Constructs a new instance of the calculator.
449
356
  *
450
- * @param beatmap The beatmap to calculate. This beatmap will be deep-cloned to prevent reference changes.
357
+ * @param beatmap The beatmap to calculate.
451
358
  */
452
359
  constructor(beatmap: Beatmap);
453
360
  /**
@@ -468,18 +375,17 @@ declare abstract class DifficultyCalculator {
468
375
  calculate(options?: DifficultyCalculationOptions): this;
469
376
  /**
470
377
  * Generates difficulty hitobjects for this calculator.
378
+ *
379
+ * @param beatmap The beatmap to generate difficulty hitobjects from.
380
+ * @param clockRate The clock rate of the beatmap.
471
381
  */
472
- generateDifficultyHitObjects(): void;
473
- /**
474
- * Performs some pre-processing before proceeding with difficulty calculation.
475
- */
476
- protected preProcess(): void;
382
+ protected abstract generateDifficultyHitObjects(beatmap: Beatmap, clockRate: number): THitObject[];
477
383
  /**
478
384
  * Calculates the skills provided.
479
385
  *
480
386
  * @param skills The skills to calculate.
481
387
  */
482
- protected calculateSkills(...skills: StrainSkill[]): void;
388
+ protected calculateSkills(...skills: Skill[]): void;
483
389
  /**
484
390
  * Calculates the total star rating of the beatmap and stores it in this instance.
485
391
  */
@@ -495,11 +401,21 @@ declare abstract class DifficultyCalculator {
495
401
  /**
496
402
  * Creates skills to be calculated.
497
403
  */
498
- protected abstract createSkills(): StrainSkill[];
404
+ protected abstract createSkills(): Skill[];
405
+ /**
406
+ * Obtains the clock rate of the beatmap.
407
+ *
408
+ * @param options The options to obtain the clock rate with.
409
+ * @returns The clock rate of the beatmap.
410
+ */
411
+ protected calculateClockRate(options?: DifficultyCalculationOptions): number;
499
412
  /**
500
413
  * Populates the stored difficulty attributes with necessary data.
414
+ *
415
+ * @param beatmap The beatmap to populate the attributes with.
416
+ * @param clockRate The clock rate of the beatmap.
501
417
  */
502
- protected populateDifficultyAttributes(): void;
418
+ protected populateDifficultyAttributes(beatmap: Beatmap, clockRate: number): void;
503
419
  /**
504
420
  * Calculates the star rating value of a difficulty.
505
421
  *
@@ -507,128 +423,248 @@ declare abstract class DifficultyCalculator {
507
423
  */
508
424
  protected starValue(difficulty: number): number;
509
425
  /**
510
- * Calculates the base performance value of a difficulty rating.
426
+ * Calculates the base performance value of a difficulty rating.
427
+ *
428
+ * @param rating The difficulty rating.
429
+ */
430
+ protected basePerformanceValue(rating: number): number;
431
+ }
432
+
433
+ /**
434
+ * Represents a slider that is considered difficult.
435
+ *
436
+ * This structure is a part of difficulty attributes and can be cached.
437
+ */
438
+ interface DifficultSlider {
439
+ /**
440
+ * The index of the slider in the beatmap.
441
+ */
442
+ readonly index: number;
443
+ /**
444
+ * The difficulty rating of this slider compared to other sliders, based on the velocity of the slider.
445
+ *
446
+ * A value closer to 1 indicates that this slider is more difficult compared to most sliders.
447
+ *
448
+ * A value closer to 0 indicates that this slider is easier compared to most sliders.
449
+ */
450
+ readonly difficultyRating: number;
451
+ }
452
+
453
+ /**
454
+ * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
455
+ * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
456
+ */
457
+ declare abstract class StrainSkill extends Skill {
458
+ /**
459
+ * Strain peaks are stored here.
460
+ */
461
+ readonly strainPeaks: number[];
462
+ /**
463
+ * The number of sections with the highest strains, which the peak strain reductions will apply to.
464
+ * This is done in order to decrease their impact on the overall difficulty of the map for this skill.
465
+ */
466
+ protected abstract readonly reducedSectionCount: number;
467
+ /**
468
+ * The baseline multiplier applied to the section with the biggest strain.
469
+ */
470
+ protected abstract readonly reducedSectionBaseline: number;
471
+ /**
472
+ * Determines how quickly strain decays for the given skill.
473
+ *
474
+ * For example, a value of 0.15 indicates that strain decays to 15% of its original value in one second.
475
+ */
476
+ protected abstract readonly strainDecayBase: number;
477
+ protected readonly _objectStrains: number[];
478
+ protected difficulty: number;
479
+ /**
480
+ * The strains of hitobjects.
481
+ */
482
+ get objectStrains(): readonly number[];
483
+ private readonly sectionLength;
484
+ private currentStrain;
485
+ private currentSectionPeak;
486
+ private currentSectionEnd;
487
+ process(current: DifficultyHitObject): void;
488
+ /**
489
+ * Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty.
490
+ */
491
+ saveCurrentPeak(): void;
492
+ /**
493
+ * Returns the number of strains weighed against the top strain.
494
+ *
495
+ * The result is scaled by clock rate as it affects the total number of strains.
496
+ */
497
+ countDifficultStrains(): number;
498
+ /**
499
+ * Calculates strain decay for a specified time frame.
500
+ *
501
+ * @param ms The time frame to calculate.
502
+ */
503
+ protected strainDecay(ms: number): number;
504
+ /**
505
+ * Calculates the starting time of a strain section at an object.
506
+ *
507
+ * @param current The object at which the strain section starts.
508
+ * @returns The start time of the strain section.
509
+ */
510
+ protected calculateCurrentSectionStart(current: DifficultyHitObject): number;
511
+ /**
512
+ * Calculates the strain value at a hitobject.
513
+ *
514
+ * @param current The hitobject to calculate.
515
+ */
516
+ protected abstract strainValueAt(current: DifficultyHitObject): number;
517
+ /**
518
+ * Saves the current strain to a hitobject.
519
+ */
520
+ protected abstract saveToHitObject(current: DifficultyHitObject): void;
521
+ /**
522
+ * Retrieves the peak strain at a point in time.
523
+ *
524
+ * @param time The time to retrieve the peak strain at.
525
+ * @param current The current hit object.
526
+ * @returns The peak strain.
527
+ */
528
+ protected abstract calculateInitialStrain(time: number, current: DifficultyHitObject): number;
529
+ /**
530
+ * Sets the initial strain level for a new section.
531
+ *
532
+ * @param time The beginning of the new section in milliseconds.
533
+ * @param current The current hitobject.
534
+ */
535
+ private startNewSectionFrom;
536
+ }
537
+
538
+ /**
539
+ * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
540
+ * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
541
+ */
542
+ declare abstract class DroidSkill extends StrainSkill {
543
+ /**
544
+ * The bonus multiplier that is given for a sequence of notes of equal difficulty.
545
+ */
546
+ protected abstract readonly starsPerDouble: number;
547
+ process(current: DifficultyHitObject): void;
548
+ difficultyValue(): number;
549
+ /**
550
+ * Gets the strain of a hitobject.
511
551
  *
512
- * @param rating The difficulty rating.
552
+ * @param current The hitobject to get the strain from.
553
+ * @returns The strain of the hitobject.
513
554
  */
514
- protected basePerformanceValue(rating: number): number;
555
+ protected abstract getObjectStrain(current: DifficultyHitObject): number;
556
+ protected calculateCurrentSectionStart(current: DifficultyHitObject): number;
515
557
  }
516
558
 
517
559
  /**
518
- * A converter used to convert normal hitobjects into difficulty hitobjects.
560
+ * Represents an osu!droid hit object with difficulty calculation values.
519
561
  */
520
- declare class DifficultyHitObjectCreator {
562
+ declare class DroidDifficultyHitObject extends DifficultyHitObject {
521
563
  /**
522
- * The threshold for small circle buff for osu!droid.
564
+ * The tap strain generated by the hitobject.
523
565
  */
524
- private readonly DROID_CIRCLESIZE_BUFF_THRESHOLD;
566
+ tapStrain: number;
525
567
  /**
526
- * The threshold for small circle buff for osu!standard.
568
+ * The tap strain generated by the hitobject if `strainTime` isn't modified by
569
+ * OD. This is used in three-finger detection.
527
570
  */
528
- private readonly PC_CIRCLESIZE_BUFF_THRESHOLD;
571
+ originalTapStrain: number;
529
572
  /**
530
- * The gamemode this creator is creating for.
573
+ * The rhythm strain generated by the hitobject.
531
574
  */
532
- private mode;
575
+ rhythmStrain: number;
533
576
  /**
534
- * The base normalized radius of hitobjects.
577
+ * The flashlight strain generated by the hitobject if sliders are considered.
535
578
  */
536
- private readonly normalizedRadius;
537
- private maximumSliderRadius;
538
- private readonly assumedSliderRadius;
539
- private readonly minDeltaTime;
579
+ flashlightStrainWithSliders: number;
540
580
  /**
541
- * Generates difficulty hitobjects for difficulty calculation.
581
+ * The flashlight strain generated by the hitobject if sliders are not considered.
542
582
  */
543
- generateDifficultyObjects(params: {
544
- objects: readonly HitObject[];
545
- circleSize: number;
546
- mods: Mod[];
547
- speedMultiplier: number;
548
- mode: Modes;
549
- preempt?: number;
550
- }): DifficultyHitObject[];
583
+ flashlightStrainWithoutSliders: number;
551
584
  /**
552
- * Calculates a slider's cursor position.
585
+ * The visual strain generated by the hitobject if sliders are considered.
553
586
  */
554
- private calculateSliderCursorPosition;
587
+ visualStrainWithSliders: number;
555
588
  /**
556
- * Gets the scaling factor of a radius.
557
- *
558
- * @param radius The radius to get the scaling factor from.
589
+ * The visual strain generated by the hitobject if sliders are not considered.
559
590
  */
560
- private getScalingFactor;
591
+ visualStrainWithoutSliders: number;
561
592
  /**
562
- * Returns the end cursor position of a hitobject.
593
+ * The note density of the hitobject.
563
594
  */
564
- private getEndCursorPosition;
565
- private applyToOverlappingFactor;
566
- }
567
-
568
- /**
569
- * Represents a slider that is considered difficult.
570
- *
571
- * This structure is a part of difficulty attributes and can be cached.
572
- */
573
- interface DifficultSlider {
595
+ noteDensity: number;
574
596
  /**
575
- * The index of the slider in the beatmap.
597
+ * The overlapping factor of the hitobject.
598
+ *
599
+ * This is used to scale visual skill.
576
600
  */
577
- readonly index: number;
601
+ overlappingFactor: number;
578
602
  /**
579
- * The difficulty rating of this slider compared to other sliders, based on the velocity of the slider.
580
- *
581
- * A value closer to 1 indicates that this slider is more difficult compared to most sliders.
603
+ * Adjusted preempt time of the hitobject, taking speed multiplier into account.
604
+ */
605
+ readonly timePreempt: number;
606
+ private readonly radiusBuffThreshold;
607
+ protected readonly mode = Modes.droid;
608
+ protected readonly maximumSliderRadius: number;
609
+ protected get scalingFactor(): number;
610
+ /**
611
+ * Note: You **must** call `computeProperties` at some point due to how TypeScript handles
612
+ * overridden properties (see [this](https://github.com/microsoft/TypeScript/issues/1617) GitHub issue).
582
613
  *
583
- * A value closer to 0 indicates that this slider is easier compared to most sliders.
614
+ * @param object The underlying hitobject.
615
+ * @param lastObject The hitobject before this hitobject.
616
+ * @param lastLastObject The hitobject before the last hitobject.
617
+ * @param difficultyHitObjects All difficulty hitobjects in the processed beatmap.
618
+ * @param clockRate The clock rate of the beatmap.
584
619
  */
585
- readonly difficultyRating: number;
586
- }
587
-
588
- /**
589
- * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
590
- * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
591
- */
592
- declare abstract class DroidSkill extends StrainSkill {
620
+ constructor(object: PlaceableHitObject, lastObject: PlaceableHitObject | null, lastLastObject: PlaceableHitObject | null, difficultyHitObjects: readonly DifficultyHitObject[], clockRate: number);
621
+ computeProperties(clockRate: number, hitObjects: readonly PlaceableHitObject[]): void;
593
622
  /**
594
- * The bonus multiplier that is given for a sequence of notes of equal difficulty.
623
+ * Determines whether this hitobject is considered overlapping with the hitobject before it.
624
+ *
625
+ * Keep in mind that "overlapping" in this case is overlapping to the point where both hitobjects
626
+ * can be hit with just a single tap in osu!droid.
627
+ *
628
+ * In the case of sliders, it is considered overlapping if all nested hitobjects can be hit with
629
+ * one aim motion.
630
+ *
631
+ * @param considerDistance Whether to consider the distance between both hitobjects.
632
+ * @returns Whether the hitobject is considered overlapping.
595
633
  */
596
- protected abstract readonly starsPerDouble: number;
597
- difficultyValue(): number;
634
+ isOverlapping(considerDistance: boolean): boolean;
635
+ private setVisuals;
636
+ private applyToOverlappingFactor;
598
637
  }
599
638
 
600
639
  /**
601
640
  * Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
602
641
  */
603
642
  declare class DroidAim extends DroidSkill {
604
- protected readonly skillMultiplier: number;
605
- protected readonly strainDecayBase: number;
606
- protected readonly reducedSectionCount: number;
607
- protected readonly reducedSectionBaseline: number;
608
- protected readonly starsPerDouble: number;
643
+ protected readonly strainDecayBase = 0.15;
644
+ protected readonly reducedSectionCount = 10;
645
+ protected readonly reducedSectionBaseline = 0.75;
646
+ protected readonly starsPerDouble = 1.05;
647
+ private readonly skillMultiplier;
609
648
  private readonly withSliders;
649
+ private currentAimStrain;
610
650
  constructor(mods: Mod[], withSliders: boolean);
611
- /**
612
- * @param current The hitobject to calculate.
613
- */
614
- protected strainValueAt(current: DifficultyHitObject): number;
651
+ protected strainValueAt(current: DroidDifficultyHitObject): number;
652
+ protected calculateInitialStrain(time: number, current: DroidDifficultyHitObject): number;
653
+ protected getObjectStrain(): number;
615
654
  /**
616
655
  * @param current The hitobject to save to.
617
656
  */
618
- protected saveToHitObject(current: DifficultyHitObject): void;
657
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
619
658
  }
620
659
 
621
660
  /**
622
661
  * An evaluator for calculating osu!droid Aim skill.
623
662
  */
624
663
  declare abstract class DroidAimEvaluator extends AimEvaluator {
625
- protected static readonly wideAngleMultiplier: number;
626
- protected static readonly sliderMultiplier: number;
627
- protected static readonly velocityChangeMultiplier: number;
628
- /**
629
- * Spacing threshold for a single hitobject spacing.
630
- */
631
- private static readonly SINGLE_SPACING_THRESHOLD;
664
+ protected static readonly wideAngleMultiplier = 1.65;
665
+ protected static readonly sliderMultiplier = 1.5;
666
+ protected static readonly velocityChangeMultiplier = 0.85;
667
+ private static readonly singleSpacingThreshold;
632
668
  private static readonly minSpeedBonus;
633
669
  /**
634
670
  * Evaluates the difficulty of aiming the current object, based on:
@@ -641,15 +677,15 @@ declare abstract class DroidAimEvaluator extends AimEvaluator {
641
677
  * @param current The current object.
642
678
  * @param withSliders Whether to take slider difficulty into account.
643
679
  */
644
- static evaluateDifficultyOf(current: DifficultyHitObject, withSliders: boolean): number;
680
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, withSliders: boolean): number;
645
681
  /**
646
- * Calculates the aim strain of a hitobject.
682
+ * Calculates the snap aim strain of a hitobject.
647
683
  */
648
- private static aimStrainOf;
684
+ private static snapAimStrainOf;
649
685
  /**
650
- * Calculates the movement strain of a hitobject.
686
+ * Calculates the flow aim strain of a hitobject.
651
687
  */
652
- private static movementStrainOf;
688
+ private static flowAimStrainOf;
653
689
  }
654
690
 
655
691
  /**
@@ -668,6 +704,40 @@ interface DroidDifficultyAttributes extends DifficultyAttributes {
668
704
  * The difficulty corresponding to the visual skill.
669
705
  */
670
706
  visualDifficulty: number;
707
+ /**
708
+ * The amount of strains that are considered difficult with respect to the tap skill.
709
+ */
710
+ tapDifficultStrainCount: number;
711
+ /**
712
+ * The amount of strains that are considered difficult with respect to the flashlight skill.
713
+ */
714
+ flashlightDifficultStrainCount: number;
715
+ /**
716
+ * The amount of strains that are considered difficult with respect to the visual skill.
717
+ */
718
+ visualDifficultStrainCount: number;
719
+ /**
720
+ * The average delta time of speed objects.
721
+ */
722
+ averageSpeedDeltaTime: number;
723
+ /**
724
+ * Describes how much of tap difficulty is contributed by notes that are "vibroable".
725
+ *
726
+ * A value closer to 1 indicates most of tap difficulty is contributed by notes that are not "vibroable".
727
+ *
728
+ * A value closer to 0 indicates most of tap difficulty is contributed by notes that are "vibroable".
729
+ */
730
+ vibroFactor: number;
731
+ }
732
+
733
+ /**
734
+ * Represents options for osu!droid difficulty calculation.
735
+ */
736
+ interface DroidDifficultyCalculationOptions extends DifficultyCalculationOptions {
737
+ /**
738
+ * Whether to calculate for old statistics (1.6.7 and older). Defaults to `false`.
739
+ */
740
+ readonly oldStatistics?: boolean;
671
741
  }
672
742
 
673
743
  /**
@@ -693,6 +763,10 @@ interface HighStrainSection {
693
763
  * as doing some analysis using the replay of a score.
694
764
  */
695
765
  interface ExtendedDroidDifficultyAttributes extends DroidDifficultyAttributes {
766
+ /**
767
+ * The mode of the difficulty calculation.
768
+ */
769
+ mode: "live";
696
770
  /**
697
771
  * Possible sections at which the player can use three fingers on.
698
772
  */
@@ -728,36 +802,38 @@ interface ExtendedDroidDifficultyAttributes extends DroidDifficultyAttributes {
728
802
  /**
729
803
  * A difficulty calculator for osu!droid gamemode.
730
804
  */
731
- declare class DroidDifficultyCalculator extends DifficultyCalculator {
805
+ declare class DroidDifficultyCalculator extends DifficultyCalculator<DroidDifficultyHitObject, DroidDifficultyAttributes> {
732
806
  /**
733
807
  * The aim star rating of the beatmap.
734
808
  */
735
- aim: number;
809
+ get aim(): number;
736
810
  /**
737
811
  * The tap star rating of the beatmap.
738
812
  */
739
- tap: number;
813
+ get tap(): number;
740
814
  /**
741
815
  * The rhythm star rating of the beatmap.
742
816
  */
743
- rhythm: number;
817
+ get rhythm(): number;
744
818
  /**
745
819
  * The flashlight star rating of the beatmap.
746
820
  */
747
- flashlight: number;
821
+ get flashlight(): number;
748
822
  /**
749
823
  * The visual star rating of the beatmap.
750
824
  */
751
- visual: number;
825
+ get visual(): number;
752
826
  /**
753
827
  * The strain threshold to start detecting for possible three-fingered section.
754
828
  *
755
829
  * Increasing this number will result in less sections being flagged.
756
830
  */
757
- static readonly threeFingerStrainThreshold: number;
831
+ static readonly threeFingerStrainThreshold = 175;
758
832
  readonly attributes: ExtendedDroidDifficultyAttributes;
759
- protected readonly difficultyMultiplier: number;
760
- protected readonly mode: Modes;
833
+ get cacheableAttributes(): CacheableDifficultyAttributes<DroidDifficultyAttributes>;
834
+ protected readonly difficultyMultiplier = 0.18;
835
+ protected readonly mode = Modes.droid;
836
+ calculate(options?: DroidDifficultyCalculationOptions): this;
761
837
  /**
762
838
  * Calculates the aim star rating of the beatmap and stores it in this instance.
763
839
  */
@@ -781,8 +857,9 @@ declare class DroidDifficultyCalculator extends DifficultyCalculator {
781
857
  calculateTotal(): void;
782
858
  calculateAll(): void;
783
859
  toString(): string;
784
- protected preProcess(): void;
860
+ protected generateDifficultyHitObjects(beatmap: Beatmap, clockRate: number): DroidDifficultyHitObject[];
785
861
  protected createSkills(): DroidSkill[];
862
+ protected calculateClockRate(options?: DroidDifficultyCalculationOptions): number;
786
863
  /**
787
864
  * Called after aim skill calculation.
788
865
  *
@@ -797,21 +874,14 @@ declare class DroidDifficultyCalculator extends DifficultyCalculator {
797
874
  /**
798
875
  * Called after tap skill calculation.
799
876
  *
800
- * @param tapSkill The tap skill.
877
+ * @param tapSkillCheese The tap skill that considers cheesing.
878
+ * @param tapSkillVibro The tap skill that considers vibro.
801
879
  */
802
880
  private postCalculateTap;
803
881
  /**
804
- * Calculates speed-related attributes.
805
- */
806
- private calculateSpeedAttributes;
807
- /**
808
- * Calculates the sum of strains for possible three-fingered sections.
809
- *
810
- * @param firstObjectIndex The index of the first object in the section.
811
- * @param lastObjectIndex The index of the last object in the section.
812
- * @returns The summed strain of the section.
882
+ * Calculates tap-related attributes.
813
883
  */
814
- private calculateThreeFingerSummedStrain;
884
+ private calculateTapAttributes;
815
885
  /**
816
886
  * Called after rhythm skill calculation.
817
887
  *
@@ -838,19 +908,20 @@ declare class DroidDifficultyCalculator extends DifficultyCalculator {
838
908
  * Represents the skill required to memorize and hit every object in a beatmap with the Flashlight mod enabled.
839
909
  */
840
910
  declare class DroidFlashlight extends DroidSkill {
841
- protected readonly skillMultiplier: number;
842
- protected readonly strainDecayBase: number;
843
- protected readonly reducedSectionCount: number;
844
- protected readonly reducedSectionBaseline: number;
845
- protected readonly starsPerDouble: number;
911
+ protected readonly strainDecayBase = 0.15;
912
+ protected readonly reducedSectionCount = 0;
913
+ protected readonly reducedSectionBaseline = 1;
914
+ protected readonly starsPerDouble = 1.06;
915
+ private readonly skillMultiplier;
846
916
  private readonly isHidden;
847
917
  private readonly withSliders;
918
+ private currentFlashlightStrain;
848
919
  constructor(mods: Mod[], withSliders: boolean);
849
- /**
850
- * @param current The hitobject to calculate.
851
- */
852
- protected strainValueAt(current: DifficultyHitObject): number;
853
- protected saveToHitObject(current: DifficultyHitObject): void;
920
+ protected strainValueAt(current: DroidDifficultyHitObject): number;
921
+ protected calculateInitialStrain(time: number, current: DifficultyHitObject): number;
922
+ protected getObjectStrain(): number;
923
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
924
+ difficultyValue(): number;
854
925
  }
855
926
 
856
927
  /**
@@ -883,7 +954,7 @@ declare abstract class DroidFlashlightEvaluator extends FlashlightEvaluator {
883
954
  * @param isHiddenMod Whether the Hidden mod is enabled.
884
955
  * @param withSliders Whether to take slider difficulty into account.
885
956
  */
886
- static evaluateDifficultyOf(current: DifficultyHitObject, isHiddenMod: boolean, withSliders: boolean): number;
957
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, isHiddenMod: boolean, withSliders: boolean): number;
887
958
  }
888
959
 
889
960
  /**
@@ -923,7 +994,7 @@ interface PerformanceCalculationOptions {
923
994
  /**
924
995
  * The base class of performance calculators.
925
996
  */
926
- declare abstract class PerformanceCalculator {
997
+ declare abstract class PerformanceCalculator<T extends DifficultyAttributes> {
927
998
  /**
928
999
  * The overall performance value.
929
1000
  */
@@ -935,11 +1006,7 @@ declare abstract class PerformanceCalculator {
935
1006
  /**
936
1007
  * The difficulty attributes that is being calculated.
937
1008
  */
938
- abstract readonly difficultyAttributes: DifficultyAttributes;
939
- /**
940
- * Penalty for combo breaks.
941
- */
942
- protected comboPenalty: number;
1009
+ readonly difficultyAttributes: T;
943
1010
  /**
944
1011
  * The global multiplier to be applied to the final performance value.
945
1012
  *
@@ -958,6 +1025,10 @@ declare abstract class PerformanceCalculator {
958
1025
  * Nerf factor used for nerfing beatmaps with very likely dropped sliderends.
959
1026
  */
960
1027
  protected sliderNerfFactor: number;
1028
+ /**
1029
+ * @param difficultyAttributes The difficulty attributes to calculate.
1030
+ */
1031
+ constructor(difficultyAttributes: T | CacheableDifficultyAttributes<T>);
961
1032
  /**
962
1033
  * Calculates the performance points of the beatmap.
963
1034
  *
@@ -977,7 +1048,7 @@ declare abstract class PerformanceCalculator {
977
1048
  /**
978
1049
  * Calculates the total performance value of the beatmap and stores it in this instance.
979
1050
  */
980
- protected abstract calculateTotalValue(): void;
1051
+ protected abstract calculateTotalValue(): number;
981
1052
  /**
982
1053
  * The total hits that can be done in the beatmap.
983
1054
  */
@@ -996,16 +1067,31 @@ declare abstract class PerformanceCalculator {
996
1067
  * @param options Options for performance calculation.
997
1068
  */
998
1069
  protected handleOptions(options?: PerformanceCalculationOptions): void;
1070
+ /**
1071
+ * Calculates a strain-based miss penalty.
1072
+ *
1073
+ * Strain-based miss penalty assumes that a player will miss on the hardest parts of a map,
1074
+ * so we use the amount of relatively difficult sections to adjust miss penalty
1075
+ * to make it more punishing on maps with lower amount of hard sections.
1076
+ */
1077
+ protected calculateStrainBasedMissPenalty(difficultStrainCount: number): number;
999
1078
  /**
1000
1079
  * Calculates the amount of misses + sliderbreaks from combo.
1001
1080
  */
1002
1081
  private calculateEffectiveMissCount;
1082
+ /**
1083
+ * Determines whether an attribute is a cacheable attribute.
1084
+ *
1085
+ * @param attributes The attributes to check.
1086
+ * @returns Whether the attributes are cacheable.
1087
+ */
1088
+ private isCacheableAttribute;
1003
1089
  }
1004
1090
 
1005
1091
  /**
1006
1092
  * A performance points calculator that calculates performance points for osu!droid gamemode.
1007
1093
  */
1008
- declare class DroidPerformanceCalculator extends PerformanceCalculator {
1094
+ declare class DroidPerformanceCalculator extends PerformanceCalculator<DroidDifficultyAttributes> {
1009
1095
  /**
1010
1096
  * The aim performance value.
1011
1097
  */
@@ -1058,19 +1144,14 @@ declare class DroidPerformanceCalculator extends PerformanceCalculator {
1058
1144
  * Can be properly obtained by analyzing the replay associated with the score.
1059
1145
  */
1060
1146
  get visualSliderCheesePenalty(): number;
1061
- readonly difficultyAttributes: DroidDifficultyAttributes;
1062
1147
  protected finalMultiplier: number;
1063
- protected readonly mode: Modes;
1148
+ protected readonly mode = Modes.droid;
1064
1149
  private _aimSliderCheesePenalty;
1065
1150
  private _flashlightSliderCheesePenalty;
1066
1151
  private _visualSliderCheesePenalty;
1067
1152
  private _tapPenalty;
1068
1153
  private _deviation;
1069
1154
  private _tapDeviation;
1070
- /**
1071
- * @param difficultyAttributes The difficulty attributes to calculate.
1072
- */
1073
- constructor(difficultyAttributes: DroidDifficultyAttributes);
1074
1155
  /**
1075
1156
  * Applies a tap penalty value to this calculator.
1076
1157
  *
@@ -1104,7 +1185,7 @@ declare class DroidPerformanceCalculator extends PerformanceCalculator {
1104
1185
  */
1105
1186
  applyVisualSliderCheesePenalty(value: number): void;
1106
1187
  protected calculateValues(): void;
1107
- protected calculateTotalValue(): void;
1188
+ protected calculateTotalValue(): number;
1108
1189
  protected handleOptions(options?: PerformanceCalculationOptions): void;
1109
1190
  /**
1110
1191
  * Calculates the aim performance value of the beatmap.
@@ -1126,6 +1207,19 @@ declare class DroidPerformanceCalculator extends PerformanceCalculator {
1126
1207
  * Calculates the visual performance value of the beatmap.
1127
1208
  */
1128
1209
  private calculateVisualValue;
1210
+ /**
1211
+ * The object-based proportional miss penalty.
1212
+ */
1213
+ private get proportionalMissPenalty();
1214
+ /**
1215
+ * Calculates the object-based length scaling based on the deviation of a player for a full
1216
+ * combo in this beatmap, taking retries into account.
1217
+ *
1218
+ * @param objectCount The amount of objects to be considered. Defaults to the amount of
1219
+ * objects in this beatmap.
1220
+ * @param punishForMemorization Whether to punish the deviation for memorization. Defaults to `false`.
1221
+ */
1222
+ private calculateDeviationBasedLengthScaling;
1129
1223
  /**
1130
1224
  * Estimates the player's tap deviation based on the OD, number of circles and sliders,
1131
1225
  * and number of 300s, 100s, 50s, and misses, assuming the player's mean hit error is 0.
@@ -1148,6 +1242,7 @@ declare class DroidPerformanceCalculator extends PerformanceCalculator {
1148
1242
  * This is fine though, since this method is only used to scale tap pp.
1149
1243
  */
1150
1244
  private calculateTapDeviation;
1245
+ private getConvertedHitWindow;
1151
1246
  toString(): string;
1152
1247
  }
1153
1248
 
@@ -1155,63 +1250,69 @@ declare class DroidPerformanceCalculator extends PerformanceCalculator {
1155
1250
  * Represents the skill required to properly follow a beatmap's rhythm.
1156
1251
  */
1157
1252
  declare class DroidRhythm extends DroidSkill {
1158
- protected readonly skillMultiplier: number;
1159
- protected readonly reducedSectionCount: number;
1160
- protected readonly reducedSectionBaseline: number;
1161
- protected readonly strainDecayBase: number;
1162
- protected readonly starsPerDouble: number;
1163
- private currentRhythm;
1164
- private readonly hitWindow;
1165
- constructor(mods: Mod[], overallDifficulty: number);
1166
- protected strainValueAt(current: DifficultyHitObject): number;
1167
- protected saveToHitObject(current: DifficultyHitObject): void;
1168
- }
1169
-
1170
- /**
1171
- * An evaluator for calculating rhythm skill.
1172
- *
1173
- * This class should be considered an "evaluating" class and not persisted.
1174
- */
1175
- declare abstract class RhythmEvaluator {
1176
- protected static readonly rhythmMultiplier: number;
1177
- protected static readonly historyTimeMax: number;
1253
+ protected readonly reducedSectionCount = 5;
1254
+ protected readonly reducedSectionBaseline = 0.75;
1255
+ protected readonly strainDecayBase = 0.3;
1256
+ protected readonly starsPerDouble = 1.75;
1257
+ private currentRhythmStrain;
1258
+ private currentRhythmMultiplier;
1259
+ protected strainValueAt(current: DroidDifficultyHitObject): number;
1260
+ protected calculateInitialStrain(time: number, current: DroidDifficultyHitObject): number;
1261
+ protected getObjectStrain(): number;
1262
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
1178
1263
  }
1179
1264
 
1180
1265
  /**
1181
1266
  * An evaluator for calculating osu!droid Rhythm skill.
1182
1267
  */
1183
- declare abstract class DroidRhythmEvaluator extends RhythmEvaluator {
1268
+ declare abstract class DroidRhythmEvaluator {
1269
+ private static readonly historyTimeMax;
1270
+ private static readonly historyObjectsMax;
1271
+ private static readonly rhythmOverallMultiplier;
1272
+ private static readonly rhythmRatioMultiplier;
1184
1273
  /**
1185
1274
  * Calculates a rhythm multiplier for the difficulty of the tap associated
1186
1275
  * with historic data of the current object.
1187
1276
  *
1188
1277
  * @param current The current object.
1189
- * @param greatWindow The great hit window of the current object.
1190
1278
  */
1191
- static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number): number;
1279
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject): number;
1192
1280
  }
1193
1281
 
1194
1282
  /**
1195
1283
  * Represents the skill required to press keys or tap with regards to keeping up with the speed at which objects need to be hit.
1196
1284
  */
1197
1285
  declare class DroidTap extends DroidSkill {
1198
- protected readonly skillMultiplier: number;
1199
- protected readonly reducedSectionCount: number;
1200
- protected readonly reducedSectionBaseline: number;
1201
- protected readonly strainDecayBase: number;
1202
- protected readonly starsPerDouble: number;
1286
+ protected readonly reducedSectionCount = 10;
1287
+ protected readonly reducedSectionBaseline = 0.75;
1288
+ protected readonly strainDecayBase = 0.3;
1289
+ protected readonly starsPerDouble = 1.1;
1203
1290
  private currentTapStrain;
1204
- private currentOriginalTapStrain;
1205
- private readonly greatWindow;
1206
- constructor(mods: Mod[], overallDifficulty: number);
1291
+ private currentRhythmMultiplier;
1292
+ private readonly skillMultiplier;
1293
+ private readonly considerCheesability;
1294
+ private readonly strainTimeCap?;
1295
+ private readonly _objectDeltaTimes;
1207
1296
  /**
1208
- * @param current The hitobject to calculate.
1297
+ * The delta time of hitobjects.
1298
+ */
1299
+ get objectDeltaTimes(): readonly number[];
1300
+ constructor(mods: Mod[], considerCheesability: boolean, strainTimeCap?: number);
1301
+ /**
1302
+ * The amount of notes that are relevant to the difficulty.
1303
+ */
1304
+ relevantNoteCount(): number;
1305
+ /**
1306
+ * The delta time relevant to the difficulty.
1209
1307
  */
1210
- protected strainValueAt(current: DifficultyHitObject): number;
1308
+ relevantDeltaTime(): number;
1309
+ protected strainValueAt(current: DroidDifficultyHitObject): number;
1310
+ protected calculateInitialStrain(time: number, current: DroidDifficultyHitObject): number;
1311
+ protected getObjectStrain(): number;
1211
1312
  /**
1212
1313
  * @param current The hitobject to save to.
1213
1314
  */
1214
- protected saveToHitObject(current: DifficultyHitObject): void;
1315
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
1215
1316
  }
1216
1317
 
1217
1318
  /**
@@ -1232,33 +1333,39 @@ declare abstract class DroidTapEvaluator extends SpeedEvaluator {
1232
1333
  *
1233
1334
  * - time between pressing the previous and current object,
1234
1335
  * - distance between those objects,
1235
- * - and how easily they can be cheesed.
1336
+ * - how easily they can be cheesed,
1337
+ * - and the strain time cap.
1236
1338
  *
1237
1339
  * @param current The current object.
1238
1340
  * @param greatWindow The great hit window of the current object.
1239
1341
  * @param considerCheesability Whether to consider cheesability.
1342
+ * @param strainTimeCap The strain time to cap the object's strain time to.
1240
1343
  */
1241
- static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number, considerCheesability: boolean): number;
1344
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, considerCheesability: boolean, strainTimeCap?: number): number;
1242
1345
  }
1243
1346
 
1244
1347
  /**
1245
1348
  * Represents the skill required to read every object in the map.
1246
1349
  */
1247
1350
  declare class DroidVisual extends DroidSkill {
1248
- protected readonly starsPerDouble: number;
1249
- protected readonly reducedSectionCount: number;
1250
- protected readonly reducedSectionBaseline: number;
1251
- protected readonly skillMultiplier: number;
1252
- protected readonly strainDecayBase: number;
1351
+ protected readonly starsPerDouble = 1.025;
1352
+ protected readonly reducedSectionCount = 10;
1353
+ protected readonly reducedSectionBaseline = 0.75;
1354
+ protected readonly strainDecayBase = 0.1;
1253
1355
  private readonly isHidden;
1254
1356
  private readonly withSliders;
1357
+ private currentVisualStrain;
1358
+ private currentRhythmMultiplier;
1359
+ private readonly skillMultiplier;
1255
1360
  constructor(mods: Mod[], withSliders: boolean);
1256
- protected strainValueAt(current: DifficultyHitObject): number;
1257
- protected saveToHitObject(current: DifficultyHitObject): void;
1361
+ protected strainValueAt(current: DroidDifficultyHitObject): number;
1362
+ protected calculateInitialStrain(time: number, current: DroidDifficultyHitObject): number;
1363
+ protected getObjectStrain(): number;
1364
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
1258
1365
  }
1259
1366
 
1260
1367
  /**
1261
- * An evaluator for calculating osu!droid Visual skill.
1368
+ * An evaluator for calculating osu!droid visual skill.
1262
1369
  */
1263
1370
  declare abstract class DroidVisualEvaluator {
1264
1371
  /**
@@ -1276,7 +1383,7 @@ declare abstract class DroidVisualEvaluator {
1276
1383
  * @param isHiddenMod Whether the Hidden mod is enabled.
1277
1384
  * @param withSliders Whether to take slider difficulty into account.
1278
1385
  */
1279
- static evaluateDifficultyOf(current: DifficultyHitObject, isHiddenMod: boolean, withSliders: boolean): number;
1386
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, isHiddenMod: boolean, withSliders: boolean): number;
1280
1387
  }
1281
1388
 
1282
1389
  /**
@@ -1285,51 +1392,107 @@ declare abstract class DroidVisualEvaluator {
1285
1392
  */
1286
1393
  declare abstract class OsuSkill extends StrainSkill {
1287
1394
  /**
1288
- * The default multiplier applied to the final difficulty value after all other calculations.
1289
- *
1290
- * May be overridden via {@link difficultyMultiplier}.
1395
+ * The weight by which each strain value decays.
1291
1396
  */
1292
- static readonly defaultDifficultyMultiplier: number;
1397
+ protected abstract readonly decayWeight: number;
1398
+ difficultyValue(): number;
1399
+ }
1400
+
1401
+ /**
1402
+ * Represents an osu!standard hit object with difficulty calculation values.
1403
+ */
1404
+ declare class OsuDifficultyHitObject extends DifficultyHitObject {
1293
1405
  /**
1294
- * The final multiplier to be applied to the final difficulty value after all other calculations.
1406
+ * The speed strain generated by the hitobject.
1295
1407
  */
1296
- protected readonly difficultyMultiplier: number;
1408
+ speedStrain: number;
1297
1409
  /**
1298
- * The weight by which each strain value decays.
1410
+ * The flashlight strain generated by this hitobject.
1299
1411
  */
1300
- protected abstract readonly decayWeight: number;
1301
- difficultyValue(): number;
1412
+ flashlightStrain: number;
1413
+ private readonly radiusBuffThreshold;
1414
+ protected readonly mode = Modes.osu;
1415
+ protected get scalingFactor(): number;
1416
+ }
1417
+
1418
+ /**
1419
+ * Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
1420
+ */
1421
+ declare class OsuAim extends OsuSkill {
1422
+ protected readonly strainDecayBase = 0.15;
1423
+ protected readonly reducedSectionCount = 10;
1424
+ protected readonly reducedSectionBaseline = 0.75;
1425
+ protected readonly decayWeight = 0.9;
1426
+ private currentAimStrain;
1427
+ private readonly skillMultiplier;
1428
+ private readonly withSliders;
1429
+ constructor(mods: Mod[], withSliders: boolean);
1430
+ protected strainValueAt(current: OsuDifficultyHitObject): number;
1431
+ protected calculateInitialStrain(time: number, current: OsuDifficultyHitObject): number;
1432
+ /**
1433
+ * @param current The hitobject to save to.
1434
+ */
1435
+ protected saveToHitObject(current: OsuDifficultyHitObject): void;
1436
+ }
1437
+
1438
+ /**
1439
+ * An evaluator for calculating osu!standard Aim skill.
1440
+ */
1441
+ declare abstract class OsuAimEvaluator extends AimEvaluator {
1442
+ /**
1443
+ * Evaluates the difficulty of aiming the current object, based on:
1444
+ *
1445
+ * - cursor velocity to the current object,
1446
+ * - angle difficulty,
1447
+ * - sharp velocity increases,
1448
+ * - and slider difficulty.
1449
+ *
1450
+ * @param current The current object.
1451
+ * @param withSliders Whether to take slider difficulty into account.
1452
+ */
1453
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject, withSliders: boolean): number;
1302
1454
  }
1303
1455
 
1304
1456
  /**
1305
1457
  * Holds data that can be used to calculate osu!standard performance points.
1306
1458
  */
1307
1459
  interface OsuDifficultyAttributes extends DifficultyAttributes {
1460
+ /**
1461
+ * The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
1462
+ *
1463
+ * Rate-adjusting mods don't directly affect the approach rate difficulty value, but have a perceived effect as a result of adjusting audio timing.
1464
+ */
1465
+ approachRate: number;
1308
1466
  /**
1309
1467
  * The difficulty corresponding to the speed skill.
1310
1468
  */
1311
1469
  speedDifficulty: number;
1470
+ /**
1471
+ * The amount of strains that are considered difficult with respect to the speed skill.
1472
+ */
1473
+ speedDifficultStrainCount: number;
1312
1474
  }
1313
1475
 
1314
1476
  /**
1315
1477
  * A difficulty calculator for osu!standard gamemode.
1316
1478
  */
1317
- declare class OsuDifficultyCalculator extends DifficultyCalculator {
1479
+ declare class OsuDifficultyCalculator extends DifficultyCalculator<OsuDifficultyHitObject, OsuDifficultyAttributes> {
1318
1480
  /**
1319
1481
  * The aim star rating of the beatmap.
1320
1482
  */
1321
- aim: number;
1483
+ get aim(): number;
1322
1484
  /**
1323
1485
  * The speed star rating of the beatmap.
1324
1486
  */
1325
- speed: number;
1487
+ get speed(): number;
1326
1488
  /**
1327
1489
  * The flashlight star rating of the beatmap.
1328
1490
  */
1329
- flashlight: number;
1491
+ get flashlight(): number;
1330
1492
  readonly attributes: OsuDifficultyAttributes;
1331
- protected readonly difficultyMultiplier: number;
1332
- protected readonly mode: Modes;
1493
+ get cacheableAttributes(): CacheableDifficultyAttributes<OsuDifficultyAttributes>;
1494
+ protected readonly difficultyMultiplier = 0.0675;
1495
+ protected readonly mode = Modes.osu;
1333
1496
  /**
1334
1497
  * Calculates the aim star rating of the beatmap and stores it in this instance.
1335
1498
  */
@@ -1345,8 +1508,9 @@ declare class OsuDifficultyCalculator extends DifficultyCalculator {
1345
1508
  calculateTotal(): void;
1346
1509
  calculateAll(): void;
1347
1510
  toString(): string;
1348
- protected preProcess(): void;
1511
+ protected generateDifficultyHitObjects(beatmap: Beatmap, clockRate: number): OsuDifficultyHitObject[];
1349
1512
  protected createSkills(): OsuSkill[];
1513
+ protected populateDifficultyAttributes(beatmap: Beatmap, clockRate: number): void;
1350
1514
  /**
1351
1515
  * Called after aim skill calculation.
1352
1516
  *
@@ -1372,87 +1536,22 @@ declare class OsuDifficultyCalculator extends DifficultyCalculator {
1372
1536
  private postCalculateFlashlight;
1373
1537
  }
1374
1538
 
1375
- /**
1376
- * A difficulty calculator that calculates for both osu!droid and osu!standard gamemode.
1377
- */
1378
- declare class MapStars {
1379
- /**
1380
- * The osu!droid difficulty calculator of the beatmap.
1381
- */
1382
- readonly droid: DroidDifficultyCalculator;
1383
- /**
1384
- * The osu!standard difficulty calculator of the beatmap.
1385
- */
1386
- readonly osu: OsuDifficultyCalculator;
1387
- /**
1388
- * Constructs this instance and calculates the given beatmap's osu!droid and osu!standard difficulty.
1389
- *
1390
- * @param beatmap The beatmap to calculate.
1391
- * @param options Options for the difficulty calculation.
1392
- */
1393
- constructor(beatmap: Beatmap, options?: DifficultyCalculationOptions);
1394
- /**
1395
- * Returns a string representative of the class.
1396
- */
1397
- toString(): string;
1398
- }
1399
-
1400
- /**
1401
- * Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
1402
- */
1403
- declare class OsuAim extends OsuSkill {
1404
- protected readonly skillMultiplier: number;
1405
- protected readonly strainDecayBase: number;
1406
- protected readonly reducedSectionCount: number;
1407
- protected readonly reducedSectionBaseline: number;
1408
- protected readonly difficultyMultiplier: number;
1409
- protected readonly decayWeight: number;
1410
- private readonly withSliders;
1411
- constructor(mods: Mod[], withSliders: boolean);
1412
- /**
1413
- * @param current The hitobject to calculate.
1414
- */
1415
- protected strainValueAt(current: DifficultyHitObject): number;
1416
- /**
1417
- * @param current The hitobject to save to.
1418
- */
1419
- protected saveToHitObject(current: DifficultyHitObject): void;
1420
- }
1421
-
1422
- /**
1423
- * An evaluator for calculating osu!standard Aim skill.
1424
- */
1425
- declare abstract class OsuAimEvaluator extends AimEvaluator {
1426
- /**
1427
- * Evaluates the difficulty of aiming the current object, based on:
1428
- *
1429
- * - cursor velocity to the current object,
1430
- * - angle difficulty,
1431
- * - sharp velocity increases,
1432
- * - and slider difficulty.
1433
- *
1434
- * @param current The current object.
1435
- * @param withSliders Whether to take slider difficulty into account.
1436
- */
1437
- static evaluateDifficultyOf(current: DifficultyHitObject, withSliders: boolean): number;
1438
- }
1439
-
1440
1539
  /**
1441
1540
  * Represents the skill required to memorize and hit every object in a beatmap with the Flashlight mod enabled.
1442
1541
  */
1443
1542
  declare class OsuFlashlight extends OsuSkill {
1444
- protected readonly skillMultiplier: number;
1445
- protected readonly strainDecayBase: number;
1446
- protected readonly reducedSectionCount: number;
1447
- protected readonly reducedSectionBaseline: number;
1448
- protected readonly decayWeight: number;
1543
+ protected readonly strainDecayBase = 0.15;
1544
+ protected readonly reducedSectionCount = 0;
1545
+ protected readonly reducedSectionBaseline = 1;
1546
+ protected readonly decayWeight = 1;
1547
+ private currentFlashlightStrain;
1548
+ private readonly skillMultiplier;
1449
1549
  private readonly isHidden;
1450
1550
  constructor(mods: Mod[]);
1451
- /**
1452
- * @param current The hitobject to calculate.
1453
- */
1454
- protected strainValueAt(current: DifficultyHitObject): number;
1455
- protected saveToHitObject(current: DifficultyHitObject): void;
1551
+ difficultyValue(): number;
1552
+ protected strainValueAt(current: OsuDifficultyHitObject): number;
1553
+ protected calculateInitialStrain(time: number, current: OsuDifficultyHitObject): number;
1554
+ protected saveToHitObject(current: OsuDifficultyHitObject): void;
1456
1555
  }
1457
1556
 
1458
1557
  /**
@@ -1471,13 +1570,13 @@ declare abstract class OsuFlashlightEvaluator extends FlashlightEvaluator {
1471
1570
  * @param current The current object.
1472
1571
  * @param isHiddenMod Whether the Hidden mod is enabled.
1473
1572
  */
1474
- static evaluateDifficultyOf(current: DifficultyHitObject, isHiddenMod: boolean): number;
1573
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject, isHiddenMod: boolean): number;
1475
1574
  }
1476
1575
 
1477
1576
  /**
1478
1577
  * A performance points calculator that calculates performance points for osu!standard gamemode.
1479
1578
  */
1480
- declare class OsuPerformanceCalculator extends PerformanceCalculator {
1579
+ declare class OsuPerformanceCalculator extends PerformanceCalculator<OsuDifficultyAttributes> {
1481
1580
  /**
1482
1581
  * The aim performance value.
1483
1582
  */
@@ -1494,15 +1593,12 @@ declare class OsuPerformanceCalculator extends PerformanceCalculator {
1494
1593
  * The flashlight performance value.
1495
1594
  */
1496
1595
  flashlight: number;
1497
- readonly difficultyAttributes: OsuDifficultyAttributes;
1498
1596
  protected finalMultiplier: number;
1499
- protected readonly mode: Modes;
1500
- /**
1501
- * @param difficultyAttributes The difficulty attributes to calculate.
1502
- */
1503
- constructor(difficultyAttributes: OsuDifficultyAttributes);
1597
+ protected readonly mode = Modes.osu;
1598
+ private comboPenalty;
1504
1599
  protected calculateValues(): void;
1505
- protected calculateTotalValue(): void;
1600
+ protected calculateTotalValue(): number;
1601
+ protected handleOptions(options?: PerformanceCalculationOptions): void;
1506
1602
  /**
1507
1603
  * Calculates the aim performance value of the beatmap.
1508
1604
  */
@@ -1525,39 +1621,40 @@ declare class OsuPerformanceCalculator extends PerformanceCalculator {
1525
1621
  /**
1526
1622
  * An evaluator for calculating osu!standard Rhythm skill.
1527
1623
  */
1528
- declare abstract class OsuRhythmEvaluator extends RhythmEvaluator {
1624
+ declare abstract class OsuRhythmEvaluator {
1625
+ private static readonly historyTimeMax;
1626
+ private static readonly historyObjectsMax;
1627
+ private static readonly rhythmOverallMultiplier;
1628
+ private static readonly rhythmRatioMultiplier;
1529
1629
  /**
1530
1630
  * Calculates a rhythm multiplier for the difficulty of the tap associated
1531
1631
  * with historic data of the current object.
1532
1632
  *
1533
1633
  * @param current The current object.
1534
- * @param greatWindow The great hit window of the current object.
1535
1634
  */
1536
- static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number): number;
1635
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject): number;
1537
1636
  }
1538
1637
 
1539
1638
  /**
1540
1639
  * Represents the skill required to press keys or tap with regards to keeping up with the speed at which objects need to be hit.
1541
1640
  */
1542
1641
  declare class OsuSpeed extends OsuSkill {
1543
- protected readonly skillMultiplier: number;
1544
- protected readonly strainDecayBase: number;
1545
- protected readonly reducedSectionCount: number;
1546
- protected readonly reducedSectionBaseline: number;
1547
- protected readonly difficultyMultiplier: number;
1548
- protected readonly decayWeight: number;
1642
+ protected readonly strainDecayBase = 0.3;
1643
+ protected readonly reducedSectionCount = 5;
1644
+ protected readonly reducedSectionBaseline = 0.75;
1645
+ protected readonly decayWeight = 0.9;
1549
1646
  private currentSpeedStrain;
1550
1647
  private currentRhythm;
1551
- private readonly greatWindow;
1552
- constructor(mods: Mod[], greatWindow: number);
1648
+ private readonly skillMultiplier;
1553
1649
  /**
1554
1650
  * @param current The hitobject to calculate.
1555
1651
  */
1556
- protected strainValueAt(current: DifficultyHitObject): number;
1652
+ protected strainValueAt(current: OsuDifficultyHitObject): number;
1653
+ protected calculateInitialStrain(time: number, current: OsuDifficultyHitObject): number;
1557
1654
  /**
1558
1655
  * @param current The hitobject to save to.
1559
1656
  */
1560
- protected saveToHitObject(current: DifficultyHitObject): void;
1657
+ protected saveToHitObject(current: OsuDifficultyHitObject): void;
1561
1658
  }
1562
1659
 
1563
1660
  /**
@@ -1566,8 +1663,11 @@ declare class OsuSpeed extends OsuSkill {
1566
1663
  declare abstract class OsuSpeedEvaluator extends SpeedEvaluator {
1567
1664
  /**
1568
1665
  * Spacing threshold for a single hitobject spacing.
1666
+ *
1667
+ * About 1.25 circles distance between hitobject centers.
1569
1668
  */
1570
1669
  private static readonly SINGLE_SPACING_THRESHOLD;
1670
+ private static readonly DISTANCE_MULTIPLIER;
1571
1671
  /**
1572
1672
  * Evaluates the difficulty of tapping the current object, based on:
1573
1673
  *
@@ -1576,9 +1676,8 @@ declare abstract class OsuSpeedEvaluator extends SpeedEvaluator {
1576
1676
  * - and how easily they can be cheesed.
1577
1677
  *
1578
1678
  * @param current The current object.
1579
- * @param greatWindow The great hit window of the current object.
1580
1679
  */
1581
- static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number): number;
1680
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject): number;
1582
1681
  }
1583
1682
 
1584
- export { AimEvaluator, CalculationOptions, DifficultSlider, DifficultyAttributes, DifficultyCalculationOptions, DifficultyCalculator, DifficultyHitObject, DifficultyHitObjectCreator, DroidAim, DroidAimEvaluator, DroidDifficultyAttributes, DroidDifficultyCalculator, DroidFlashlight, DroidFlashlightEvaluator, DroidPerformanceCalculator, DroidRhythm, DroidRhythmEvaluator, DroidTap, DroidTapEvaluator, DroidVisual, DroidVisualEvaluator, ExtendedDroidDifficultyAttributes, FlashlightEvaluator, HighStrainSection, MapStars, OsuAim, OsuAimEvaluator, OsuDifficultyAttributes, OsuDifficultyCalculator, OsuFlashlight, OsuFlashlightEvaluator, OsuPerformanceCalculator, OsuRhythmEvaluator, OsuSpeed, OsuSpeedEvaluator, PerformanceCalculationOptions, PerformanceCalculator, RhythmEvaluator, SpeedEvaluator, StrainPeaks };
1683
+ export { AimEvaluator, type CacheableDifficultyAttributes, type DifficultSlider, type DifficultyAttributes, type DifficultyCalculationOptions, DifficultyCalculator, DifficultyHitObject, DroidAim, DroidAimEvaluator, type DroidDifficultyAttributes, type DroidDifficultyCalculationOptions, DroidDifficultyCalculator, DroidDifficultyHitObject, DroidFlashlight, DroidFlashlightEvaluator, DroidPerformanceCalculator, DroidRhythm, DroidRhythmEvaluator, DroidTap, DroidTapEvaluator, DroidVisual, DroidVisualEvaluator, type ExtendedDroidDifficultyAttributes, FlashlightEvaluator, type HighStrainSection, OsuAim, OsuAimEvaluator, type OsuDifficultyAttributes, OsuDifficultyCalculator, OsuDifficultyHitObject, OsuFlashlight, OsuFlashlightEvaluator, OsuPerformanceCalculator, OsuRhythmEvaluator, OsuSpeed, OsuSpeedEvaluator, type PerformanceCalculationOptions, PerformanceCalculator, SpeedEvaluator, type StrainPeaks };