@rian8337/osu-difficulty-calculator 4.0.0-beta.2 → 4.0.0-beta.21

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, DifficultyStatisticsCalculatorResult, 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
  */
@@ -68,6 +58,10 @@ interface DifficultyAttributes {
68
58
  * A value closer to 0 indicates most of aim difficulty is contributed by sliders.
69
59
  */
70
60
  sliderFactor: number;
61
+ /**
62
+ * The overall clock rate that was applied to the beatmap.
63
+ */
64
+ clockRate: number;
71
65
  /**
72
66
  * The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
73
67
  *
@@ -94,20 +88,34 @@ interface DifficultyAttributes {
94
88
  spinnerCount: number;
95
89
  }
96
90
 
91
+ /**
92
+ * Represents difficulty attributes that can be cached.
93
+ */
94
+ type CacheableDifficultyAttributes<T extends DifficultyAttributes> = Omit<T, "mods"> & {
95
+ /**
96
+ * The mods which were applied to the beatmap.
97
+ */
98
+ mods: string;
99
+ };
100
+
97
101
  /**
98
102
  * Represents options for difficulty calculation.
99
103
  */
100
- interface DifficultyCalculationOptions extends CalculationOptions {
104
+ interface DifficultyCalculationOptions {
101
105
  /**
102
106
  * The modifications to apply.
103
107
  */
104
- mods?: Mod[];
108
+ readonly mods?: Mod[];
109
+ /**
110
+ * The custom speed multiplier to apply. Defaults to 1.
111
+ */
112
+ readonly customSpeedMultiplier?: number;
105
113
  }
106
114
 
107
115
  /**
108
- * Represents an osu!standard hit object with difficulty calculation values.
116
+ * Represents a hit object with difficulty calculation values.
109
117
  */
110
- declare class DifficultyHitObject {
118
+ declare abstract class DifficultyHitObject {
111
119
  /**
112
120
  * The underlying hitobject.
113
121
  */
@@ -117,19 +125,7 @@ declare class DifficultyHitObject {
117
125
  *
118
126
  * This is one less than the actual index of the hitobject in the beatmap.
119
127
  */
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;
128
+ readonly index: number;
133
129
  /**
134
130
  * The aim strain generated by the hitobject if sliders are considered.
135
131
  */
@@ -138,41 +134,10 @@ declare class DifficultyHitObject {
138
134
  * The aim strain generated by the hitobject if sliders are not considered.
139
135
  */
140
136
  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
137
  /**
153
138
  * The rhythm multiplier generated by the hitobject. This is used to alter tap strain.
154
139
  */
155
140
  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
141
  /**
177
142
  * The normalized distance from the "lazy" end position of the previous hitobject to the start position of this hitobject.
178
143
  *
@@ -213,42 +178,48 @@ declare class DifficultyHitObject {
213
178
  /**
214
179
  * The amount of milliseconds elapsed between this hitobject and the last hitobject.
215
180
  */
216
- deltaTime: number;
181
+ readonly deltaTime: number;
217
182
  /**
218
183
  * The amount of milliseconds elapsed since the start time of the previous hitobject, with a minimum of 25ms.
219
184
  */
220
- strainTime: number;
185
+ readonly strainTime: number;
221
186
  /**
222
187
  * Adjusted start time of the hitobject, taking speed multiplier into account.
223
188
  */
224
- startTime: number;
189
+ readonly startTime: number;
225
190
  /**
226
191
  * Adjusted end time of the hitobject, taking speed multiplier into account.
227
192
  */
228
- endTime: number;
193
+ readonly endTime: number;
229
194
  /**
230
- * The note density of the hitobject.
195
+ * Other hitobjects in the beatmap, including this hitobject.
231
196
  */
232
- noteDensity: number;
197
+ protected readonly hitObjects: readonly DifficultyHitObject[];
198
+ protected abstract readonly mode: Modes;
199
+ protected readonly normalizedRadius = 50;
200
+ protected readonly maximumSliderRadius: number;
201
+ protected readonly assumedSliderRadius: number;
202
+ protected readonly minDeltaTime = 25;
203
+ private readonly lastObject;
204
+ private readonly lastLastObject;
233
205
  /**
234
- * The overlapping factor of the hitobject.
206
+ * Note: You **must** call `computeProperties` at some point due to how TypeScript handles
207
+ * overridden properties (see [this](https://github.com/microsoft/TypeScript/issues/1617) GitHub issue).
235
208
  *
236
- * This is used to scale visual skill.
237
- */
238
- overlappingFactor: number;
239
- /**
240
- * Adjusted velocity of the hitobject, taking speed multiplier into account.
241
- */
242
- velocity: number;
243
- /**
244
- * Other hitobjects in the beatmap, including this hitobject.
209
+ * @param object The underlying hitobject.
210
+ * @param lastObject The hitobject before this hitobject.
211
+ * @param lastLastObject The hitobject before the last hitobject.
212
+ * @param difficultyHitObjects All difficulty hitobjects in the processed beatmap.
213
+ * @param clockRate The clock rate of the beatmap.
245
214
  */
246
- private readonly hitObjects;
215
+ protected constructor(object: PlaceableHitObject, lastObject: PlaceableHitObject | null, lastLastObject: PlaceableHitObject | null, difficultyHitObjects: readonly DifficultyHitObject[], clockRate: number);
247
216
  /**
248
- * @param object The underlying hitobject.
249
- * @param hitObjects All difficulty hitobjects in the processed beatmap.
217
+ * Computes the properties of this hitobject.
218
+ *
219
+ * @param clockRate The clock rate of the beatmap.
220
+ * @param hitObjects The hitobjects in the beatmap.
250
221
  */
251
- constructor(object: PlaceableHitObject, hitObjects: DifficultyHitObject[]);
222
+ computeProperties(clockRate: number, hitObjects: readonly PlaceableHitObject[]): void;
252
223
  /**
253
224
  * Gets the difficulty hitobject at a specific index with respect to the current
254
225
  * difficulty hitobject's index.
@@ -259,7 +230,7 @@ declare class DifficultyHitObject {
259
230
  * @returns The difficulty hitobject at the index with respect to the current
260
231
  * difficulty hitobject's index, `null` if the index is out of range.
261
232
  */
262
- previous(backwardsIndex: number): DifficultyHitObject | null;
233
+ previous(backwardsIndex: number): this | null;
263
234
  /**
264
235
  * Gets the difficulty hitobject at a specific index with respect to the current
265
236
  * difficulty hitobject's index.
@@ -270,26 +241,41 @@ declare class DifficultyHitObject {
270
241
  * @returns The difficulty hitobject at the index with respect to the current
271
242
  * difficulty hitobject's index, `null` if the index is out of range.
272
243
  */
273
- next(forwardsIndex: number): DifficultyHitObject | null;
244
+ next(forwardsIndex: number): this | null;
274
245
  /**
275
246
  * Calculates the opacity of the hitobject at a given time.
276
247
  *
277
248
  * @param time The time to calculate the hitobject's opacity at.
278
249
  * @param isHidden Whether Hidden mod is used.
279
- * @param mode The gamemode to calculate the opacity for.
280
250
  * @returns The opacity of the hitobject at the given time.
281
251
  */
282
- opacityAt(time: number, isHidden: boolean, mode: Modes): number;
252
+ opacityAt(time: number, isHidden: boolean): number;
253
+ protected abstract get scalingFactor(): number;
254
+ protected setDistances(clockRate: number): void;
255
+ private calculateSliderCursorPosition;
256
+ private getEndCursorPosition;
257
+ }
258
+
259
+ /**
260
+ * Represents the strain peaks of various calculated difficulties.
261
+ */
262
+ interface StrainPeaks {
283
263
  /**
284
- * Determines whether this hitobject is considered overlapping with the hitobject before it.
285
- *
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.
264
+ * The strain peaks of aim difficulty if sliders are considered.
291
265
  */
292
- isOverlapping(considerDistance: boolean): boolean;
266
+ aimWithSliders: number[];
267
+ /**
268
+ * The strain peaks of aim difficulty if sliders are not considered.
269
+ */
270
+ aimWithoutSliders: number[];
271
+ /**
272
+ * The strain peaks of speed difficulty.
273
+ */
274
+ speed: number[];
275
+ /**
276
+ * The strain peaks of flashlight difficulty.
277
+ */
278
+ flashlight: number[];
293
279
  }
294
280
 
295
281
  /**
@@ -316,103 +302,9 @@ declare abstract class Skill {
316
302
  }
317
303
 
318
304
  /**
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.
305
+ * The base of a difficulty calculator.
321
306
  */
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.
392
- */
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 {
307
+ declare abstract class DifficultyCalculator<THitObject extends DifficultyHitObject, TAttributes extends DifficultyAttributes> {
416
308
  /**
417
309
  * The calculated beatmap.
418
310
  */
@@ -420,7 +312,7 @@ declare abstract class DifficultyCalculator {
420
312
  /**
421
313
  * The difficulty objects of the beatmap.
422
314
  */
423
- readonly objects: DifficultyHitObject[];
315
+ readonly objects: THitObject[];
424
316
  /**
425
317
  * The modifications applied.
426
318
  */
@@ -428,20 +320,23 @@ declare abstract class DifficultyCalculator {
428
320
  /**
429
321
  * The total star rating of the beatmap.
430
322
  */
431
- total: number;
323
+ get total(): number;
432
324
  /**
433
- * The map statistics of the beatmap after modifications are applied.
325
+ * The difficulty statistics of the beatmap after modifications are applied.
434
326
  */
435
- stats: MapStats;
327
+ difficultyStatistics: DifficultyStatisticsCalculatorResult<number, number, number, number>;
436
328
  /**
437
329
  * The strain peaks of various calculated difficulties.
438
330
  */
439
331
  readonly strainPeaks: StrainPeaks;
440
332
  /**
441
- * Holds data that can be used to calculate performance points.
333
+ * The difficulty attributes that can be used to calculate performance points.
334
+ */
335
+ abstract readonly attributes: TAttributes;
336
+ /**
337
+ * The difficulty attributes that can be cached. It can also be used to calculate performance points.
442
338
  */
443
- abstract readonly attributes: DifficultyAttributes;
444
- protected readonly sectionLength: number;
339
+ abstract get cacheableAttributes(): CacheableDifficultyAttributes<TAttributes>;
445
340
  protected abstract readonly difficultyMultiplier: number;
446
341
  protected abstract readonly mode: Modes;
447
342
  /**
@@ -468,14 +363,23 @@ declare abstract class DifficultyCalculator {
468
363
  calculate(options?: DifficultyCalculationOptions): this;
469
364
  /**
470
365
  * Generates difficulty hitobjects for this calculator.
366
+ *
367
+ * @param convertedBeatmap The beatmap to generate difficulty hitobjects from.
471
368
  */
472
- generateDifficultyHitObjects(): void;
369
+ protected abstract generateDifficultyHitObjects(convertedBeatmap: Beatmap): THitObject[];
370
+ /**
371
+ * Computes the difficulty statistics of the original beatmap with respect to the used options.
372
+ *
373
+ * @param options The options to use for the difficulty statistics calculation.
374
+ * @returns The computed difficulty statistics.
375
+ */
376
+ protected abstract computeDifficultyStatistics(options?: DifficultyCalculationOptions): DifficultyStatisticsCalculatorResult<number, number, number, number>;
473
377
  /**
474
378
  * Calculates the skills provided.
475
379
  *
476
380
  * @param skills The skills to calculate.
477
381
  */
478
- protected calculateSkills(...skills: StrainSkill[]): void;
382
+ protected calculateSkills(...skills: Skill[]): void;
479
383
  /**
480
384
  * Calculates the total star rating of the beatmap and stores it in this instance.
481
385
  */
@@ -491,7 +395,7 @@ declare abstract class DifficultyCalculator {
491
395
  /**
492
396
  * Creates skills to be calculated.
493
397
  */
494
- protected abstract createSkills(): StrainSkill[];
398
+ protected abstract createSkills(): Skill[];
495
399
  /**
496
400
  * Populates the stored difficulty attributes with necessary data.
497
401
  */
@@ -511,120 +415,237 @@ declare abstract class DifficultyCalculator {
511
415
  }
512
416
 
513
417
  /**
514
- * A converter used to convert normal hitobjects into difficulty hitobjects.
418
+ * Represents a slider that is considered difficult.
419
+ *
420
+ * This structure is a part of difficulty attributes and can be cached.
515
421
  */
516
- declare class DifficultyHitObjectCreator {
422
+ interface DifficultSlider {
517
423
  /**
518
- * The threshold for small circle buff for osu!droid.
424
+ * The index of the slider in the beatmap.
519
425
  */
520
- private readonly DROID_CIRCLESIZE_BUFF_THRESHOLD;
426
+ readonly index: number;
521
427
  /**
522
- * The threshold for small circle buff for osu!standard.
428
+ * The difficulty rating of this slider compared to other sliders, based on the velocity of the slider.
429
+ *
430
+ * A value closer to 1 indicates that this slider is more difficult compared to most sliders.
431
+ *
432
+ * A value closer to 0 indicates that this slider is easier compared to most sliders.
523
433
  */
524
- private readonly PC_CIRCLESIZE_BUFF_THRESHOLD;
434
+ readonly difficultyRating: number;
435
+ }
436
+
437
+ /**
438
+ * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
439
+ * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
440
+ */
441
+ declare abstract class StrainSkill extends Skill {
525
442
  /**
526
- * The gamemode this creator is creating for.
443
+ * Strain peaks are stored here.
527
444
  */
528
- private mode;
445
+ readonly strainPeaks: number[];
529
446
  /**
530
- * The base normalized radius of hitobjects.
447
+ * The number of sections with the highest strains, which the peak strain reductions will apply to.
448
+ * This is done in order to decrease their impact on the overall difficulty of the map for this skill.
531
449
  */
532
- private readonly normalizedRadius;
533
- private maximumSliderRadius;
534
- private readonly assumedSliderRadius;
535
- private readonly minDeltaTime;
450
+ protected abstract readonly reducedSectionCount: number;
536
451
  /**
537
- * Generates difficulty hitobjects for difficulty calculation.
452
+ * The baseline multiplier applied to the section with the biggest strain.
538
453
  */
539
- generateDifficultyObjects(params: {
540
- objects: readonly HitObject[];
541
- circleSize: number;
542
- mods: Mod[];
543
- speedMultiplier: number;
544
- mode: Modes;
545
- preempt?: number;
546
- }): DifficultyHitObject[];
454
+ protected abstract readonly reducedSectionBaseline: number;
547
455
  /**
548
- * Calculates a slider's cursor position.
456
+ * Determines how quickly strain decays for the given skill.
457
+ *
458
+ * For example, a value of 0.15 indicates that strain decays to 15% of its original value in one second.
549
459
  */
550
- private calculateSliderCursorPosition;
460
+ protected abstract readonly strainDecayBase: number;
461
+ private readonly sectionLength;
462
+ private currentStrain;
463
+ private currentSectionPeak;
464
+ private currentSectionEnd;
465
+ process(current: DifficultyHitObject): void;
466
+ /**
467
+ * Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty.
468
+ */
469
+ saveCurrentPeak(): void;
470
+ /**
471
+ * Calculates strain decay for a specified time frame.
472
+ *
473
+ * @param ms The time frame to calculate.
474
+ */
475
+ protected strainDecay(ms: number): number;
476
+ /**
477
+ * Calculates the starting time of a strain section at an object.
478
+ *
479
+ * @param current The object at which the strain section starts.
480
+ * @returns The start time of the strain section.
481
+ */
482
+ protected calculateCurrentSectionStart(current: DifficultyHitObject): number;
483
+ /**
484
+ * Calculates the strain value at a hitobject.
485
+ *
486
+ * @param current The hitobject to calculate.
487
+ */
488
+ protected abstract strainValueAt(current: DifficultyHitObject): number;
489
+ /**
490
+ * Saves the current strain to a hitobject.
491
+ */
492
+ protected abstract saveToHitObject(current: DifficultyHitObject): void;
493
+ /**
494
+ * Retrieves the peak strain at a point in time.
495
+ *
496
+ * @param time The time to retrieve the peak strain at.
497
+ * @param current The current hit object.
498
+ * @returns The peak strain.
499
+ */
500
+ protected abstract calculateInitialStrain(time: number, current: DifficultyHitObject): number;
501
+ /**
502
+ * Sets the initial strain level for a new section.
503
+ *
504
+ * @param time The beginning of the new section in milliseconds.
505
+ * @param current The current hitobject.
506
+ */
507
+ private startNewSectionFrom;
508
+ }
509
+
510
+ /**
511
+ * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
512
+ * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
513
+ */
514
+ declare abstract class DroidSkill extends StrainSkill {
515
+ /**
516
+ * The bonus multiplier that is given for a sequence of notes of equal difficulty.
517
+ */
518
+ protected abstract readonly starsPerDouble: number;
519
+ protected readonly _objectStrains: number[];
520
+ /**
521
+ * The strains of hitobjects.
522
+ */
523
+ get objectStrains(): readonly number[];
524
+ /**
525
+ * Returns the number of strains weighed against the top strain.
526
+ *
527
+ * The result is scaled by clock rate as it affects the total number of strains.
528
+ */
529
+ countDifficultStrains(): number;
530
+ process(current: DifficultyHitObject): void;
531
+ difficultyValue(): number;
532
+ /**
533
+ * Gets the strain of a hitobject.
534
+ *
535
+ * @param current The hitobject to get the strain from.
536
+ * @returns The strain of the hitobject.
537
+ */
538
+ protected abstract getObjectStrain(current: DifficultyHitObject): number;
539
+ protected calculateCurrentSectionStart(current: DifficultyHitObject): number;
540
+ }
541
+
542
+ /**
543
+ * Represents an osu!droid hit object with difficulty calculation values.
544
+ */
545
+ declare class DroidDifficultyHitObject extends DifficultyHitObject {
546
+ /**
547
+ * The tap strain generated by the hitobject.
548
+ */
549
+ tapStrain: number;
550
+ /**
551
+ * The tap strain generated by the hitobject if `strainTime` isn't modified by
552
+ * OD. This is used in three-finger detection.
553
+ */
554
+ originalTapStrain: number;
555
+ /**
556
+ * The rhythm strain generated by the hitobject.
557
+ */
558
+ rhythmStrain: number;
559
+ /**
560
+ * The flashlight strain generated by the hitobject if sliders are considered.
561
+ */
562
+ flashlightStrainWithSliders: number;
563
+ /**
564
+ * The flashlight strain generated by the hitobject if sliders are not considered.
565
+ */
566
+ flashlightStrainWithoutSliders: number;
567
+ /**
568
+ * The visual strain generated by the hitobject if sliders are considered.
569
+ */
570
+ visualStrainWithSliders: number;
571
+ /**
572
+ * The visual strain generated by the hitobject if sliders are not considered.
573
+ */
574
+ visualStrainWithoutSliders: number;
575
+ /**
576
+ * The note density of the hitobject.
577
+ */
578
+ noteDensity: number;
551
579
  /**
552
- * Gets the scaling factor of a radius.
580
+ * The overlapping factor of the hitobject.
553
581
  *
554
- * @param radius The radius to get the scaling factor from.
582
+ * This is used to scale visual skill.
555
583
  */
556
- private getScalingFactor;
584
+ overlappingFactor: number;
557
585
  /**
558
- * Returns the end cursor position of a hitobject.
586
+ * Adjusted preempt time of the hitobject, taking speed multiplier into account.
559
587
  */
560
- private getEndCursorPosition;
561
- private applyToOverlappingFactor;
562
- }
563
-
564
- /**
565
- * Represents a slider that is considered difficult.
566
- *
567
- * This structure is a part of difficulty attributes and can be cached.
568
- */
569
- interface DifficultSlider {
588
+ readonly timePreempt: number;
589
+ private readonly radiusBuffThreshold;
590
+ protected readonly mode = Modes.droid;
591
+ protected readonly maximumSliderRadius: number;
592
+ protected get scalingFactor(): number;
570
593
  /**
571
- * The index of the slider in the beatmap.
594
+ * Note: You **must** call `computeProperties` at some point due to how TypeScript handles
595
+ * overridden properties (see [this](https://github.com/microsoft/TypeScript/issues/1617) GitHub issue).
596
+ *
597
+ * @param object The underlying hitobject.
598
+ * @param lastObject The hitobject before this hitobject.
599
+ * @param lastLastObject The hitobject before the last hitobject.
600
+ * @param difficultyHitObjects All difficulty hitobjects in the processed beatmap.
601
+ * @param clockRate The clock rate of the beatmap.
602
+ * @param isForceAR Whether force AR is enabled.
572
603
  */
573
- readonly index: number;
604
+ constructor(object: PlaceableHitObject, lastObject: PlaceableHitObject | null, lastLastObject: PlaceableHitObject | null, difficultyHitObjects: readonly DifficultyHitObject[], clockRate: number, isForceAR: boolean);
605
+ computeProperties(clockRate: number, hitObjects: readonly PlaceableHitObject[]): void;
574
606
  /**
575
- * The difficulty rating of this slider compared to other sliders, based on the velocity of the slider.
607
+ * Determines whether this hitobject is considered overlapping with the hitobject before it.
576
608
  *
577
- * A value closer to 1 indicates that this slider is more difficult compared to most sliders.
609
+ * Keep in mind that "overlapping" in this case is overlapping to the point where both hitobjects
610
+ * can be hit with just a single tap in osu!droid.
578
611
  *
579
- * A value closer to 0 indicates that this slider is easier compared to most sliders.
580
- */
581
- readonly difficultyRating: number;
582
- }
583
-
584
- /**
585
- * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
586
- * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
587
- */
588
- declare abstract class DroidSkill extends StrainSkill {
589
- /**
590
- * The bonus multiplier that is given for a sequence of notes of equal difficulty.
612
+ * @param considerDistance Whether to consider the distance between both hitobjects.
613
+ * @returns Whether the hitobject is considered overlapping.
591
614
  */
592
- protected abstract readonly starsPerDouble: number;
593
- difficultyValue(): number;
615
+ isOverlapping(considerDistance: boolean): boolean;
616
+ private setVisuals;
617
+ private applyToOverlappingFactor;
594
618
  }
595
619
 
596
620
  /**
597
621
  * Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
598
622
  */
599
623
  declare class DroidAim extends DroidSkill {
600
- protected readonly skillMultiplier: number;
601
- protected readonly strainDecayBase: number;
602
- protected readonly reducedSectionCount: number;
603
- protected readonly reducedSectionBaseline: number;
604
- protected readonly starsPerDouble: number;
624
+ protected readonly strainDecayBase = 0.15;
625
+ protected readonly reducedSectionCount = 10;
626
+ protected readonly reducedSectionBaseline = 0.75;
627
+ protected readonly starsPerDouble = 1.05;
628
+ private readonly skillMultiplier;
605
629
  private readonly withSliders;
630
+ private currentAimStrain;
606
631
  constructor(mods: Mod[], withSliders: boolean);
607
- /**
608
- * @param current The hitobject to calculate.
609
- */
610
- protected strainValueAt(current: DifficultyHitObject): number;
632
+ protected strainValueAt(current: DroidDifficultyHitObject): number;
633
+ protected calculateInitialStrain(time: number, current: DroidDifficultyHitObject): number;
634
+ protected getObjectStrain(): number;
611
635
  /**
612
636
  * @param current The hitobject to save to.
613
637
  */
614
- protected saveToHitObject(current: DifficultyHitObject): void;
638
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
615
639
  }
616
640
 
617
641
  /**
618
642
  * An evaluator for calculating osu!droid Aim skill.
619
643
  */
620
644
  declare abstract class DroidAimEvaluator extends AimEvaluator {
621
- protected static readonly wideAngleMultiplier: number;
622
- protected static readonly sliderMultiplier: number;
623
- protected static readonly velocityChangeMultiplier: number;
624
- /**
625
- * Spacing threshold for a single hitobject spacing.
626
- */
627
- private static readonly SINGLE_SPACING_THRESHOLD;
645
+ protected static readonly wideAngleMultiplier = 1.65;
646
+ protected static readonly sliderMultiplier = 1.5;
647
+ protected static readonly velocityChangeMultiplier = 0.85;
648
+ private static readonly singleSpacingThreshold;
628
649
  private static readonly minSpeedBonus;
629
650
  /**
630
651
  * Evaluates the difficulty of aiming the current object, based on:
@@ -637,15 +658,15 @@ declare abstract class DroidAimEvaluator extends AimEvaluator {
637
658
  * @param current The current object.
638
659
  * @param withSliders Whether to take slider difficulty into account.
639
660
  */
640
- static evaluateDifficultyOf(current: DifficultyHitObject, withSliders: boolean): number;
661
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, withSliders: boolean): number;
641
662
  /**
642
- * Calculates the aim strain of a hitobject.
663
+ * Calculates the snap aim strain of a hitobject.
643
664
  */
644
- private static aimStrainOf;
665
+ private static snapAimStrainOf;
645
666
  /**
646
- * Calculates the movement strain of a hitobject.
667
+ * Calculates the flow aim strain of a hitobject.
647
668
  */
648
- private static movementStrainOf;
669
+ private static flowAimStrainOf;
649
670
  }
650
671
 
651
672
  /**
@@ -664,6 +685,44 @@ interface DroidDifficultyAttributes extends DifficultyAttributes {
664
685
  * The difficulty corresponding to the visual skill.
665
686
  */
666
687
  visualDifficulty: number;
688
+ /**
689
+ * The amount of strains that are considered difficult with respect to the aim skill.
690
+ */
691
+ aimDifficultStrainCount: number;
692
+ /**
693
+ * The amount of strains that are considered difficult with respect to the tap skill.
694
+ */
695
+ tapDifficultStrainCount: number;
696
+ /**
697
+ * The amount of strains that are considered difficult with respect to the flashlight skill.
698
+ */
699
+ flashlightDifficultStrainCount: number;
700
+ /**
701
+ * The amount of strains that are considered difficult with respect to the visual skill.
702
+ */
703
+ visualDifficultStrainCount: number;
704
+ /**
705
+ * The average delta time of speed objects.
706
+ */
707
+ averageSpeedDeltaTime: number;
708
+ /**
709
+ * Describes how much of tap difficulty is contributed by notes that are "vibroable".
710
+ *
711
+ * A value closer to 1 indicates most of tap difficulty is contributed by notes that are not "vibroable".
712
+ *
713
+ * A value closer to 0 indicates most of tap difficulty is contributed by notes that are "vibroable".
714
+ */
715
+ vibroFactor: number;
716
+ }
717
+
718
+ /**
719
+ * Represents options for osu!droid difficulty calculation.
720
+ */
721
+ interface DroidDifficultyCalculationOptions extends DifficultyCalculationOptions {
722
+ /**
723
+ * Whether to calculate for old statistics (1.6.7 and older). Defaults to `false`.
724
+ */
725
+ readonly oldStatistics?: boolean;
667
726
  }
668
727
 
669
728
  /**
@@ -689,6 +748,10 @@ interface HighStrainSection {
689
748
  * as doing some analysis using the replay of a score.
690
749
  */
691
750
  interface ExtendedDroidDifficultyAttributes extends DroidDifficultyAttributes {
751
+ /**
752
+ * The mode of the difficulty calculation.
753
+ */
754
+ mode: "live";
692
755
  /**
693
756
  * Possible sections at which the player can use three fingers on.
694
757
  */
@@ -724,36 +787,37 @@ interface ExtendedDroidDifficultyAttributes extends DroidDifficultyAttributes {
724
787
  /**
725
788
  * A difficulty calculator for osu!droid gamemode.
726
789
  */
727
- declare class DroidDifficultyCalculator extends DifficultyCalculator {
790
+ declare class DroidDifficultyCalculator extends DifficultyCalculator<DroidDifficultyHitObject, DroidDifficultyAttributes> {
728
791
  /**
729
792
  * The aim star rating of the beatmap.
730
793
  */
731
- aim: number;
794
+ get aim(): number;
732
795
  /**
733
796
  * The tap star rating of the beatmap.
734
797
  */
735
- tap: number;
798
+ get tap(): number;
736
799
  /**
737
800
  * The rhythm star rating of the beatmap.
738
801
  */
739
- rhythm: number;
802
+ get rhythm(): number;
740
803
  /**
741
804
  * The flashlight star rating of the beatmap.
742
805
  */
743
- flashlight: number;
806
+ get flashlight(): number;
744
807
  /**
745
808
  * The visual star rating of the beatmap.
746
809
  */
747
- visual: number;
810
+ get visual(): number;
748
811
  /**
749
812
  * The strain threshold to start detecting for possible three-fingered section.
750
813
  *
751
814
  * Increasing this number will result in less sections being flagged.
752
815
  */
753
- static readonly threeFingerStrainThreshold: number;
816
+ static readonly threeFingerStrainThreshold = 175;
754
817
  readonly attributes: ExtendedDroidDifficultyAttributes;
755
- protected readonly difficultyMultiplier: number;
756
- protected readonly mode: Modes;
818
+ get cacheableAttributes(): CacheableDifficultyAttributes<DroidDifficultyAttributes>;
819
+ protected readonly difficultyMultiplier = 0.18;
820
+ protected readonly mode = Modes.droid;
757
821
  /**
758
822
  * Calculates the aim star rating of the beatmap and stores it in this instance.
759
823
  */
@@ -776,13 +840,9 @@ declare class DroidDifficultyCalculator extends DifficultyCalculator {
776
840
  calculateVisual(): void;
777
841
  calculateTotal(): void;
778
842
  calculateAll(): void;
779
- /**
780
- * Returns a string representative of the class.
781
- */
782
843
  toString(): string;
783
- /**
784
- * Creates skills to be calculated.
785
- */
844
+ protected generateDifficultyHitObjects(convertedBeatmap: Beatmap): DroidDifficultyHitObject[];
845
+ protected computeDifficultyStatistics(options?: DroidDifficultyCalculationOptions): DifficultyStatisticsCalculatorResult<number, number, number, number>;
786
846
  protected createSkills(): DroidSkill[];
787
847
  /**
788
848
  * Called after aim skill calculation.
@@ -798,21 +858,14 @@ declare class DroidDifficultyCalculator extends DifficultyCalculator {
798
858
  /**
799
859
  * Called after tap skill calculation.
800
860
  *
801
- * @param tapSkill The tap skill.
861
+ * @param tapSkillCheese The tap skill that considers cheesing.
862
+ * @param tapSkillVibro The tap skill that considers vibro.
802
863
  */
803
864
  private postCalculateTap;
804
865
  /**
805
- * Calculates speed-related attributes.
806
- */
807
- private calculateSpeedAttributes;
808
- /**
809
- * Calculates the sum of strains for possible three-fingered sections.
810
- *
811
- * @param firstObjectIndex The index of the first object in the section.
812
- * @param lastObjectIndex The index of the last object in the section.
813
- * @returns The summed strain of the section.
866
+ * Calculates tap-related attributes.
814
867
  */
815
- private calculateThreeFingerSummedStrain;
868
+ private calculateTapAttributes;
816
869
  /**
817
870
  * Called after rhythm skill calculation.
818
871
  *
@@ -839,19 +892,20 @@ declare class DroidDifficultyCalculator extends DifficultyCalculator {
839
892
  * Represents the skill required to memorize and hit every object in a beatmap with the Flashlight mod enabled.
840
893
  */
841
894
  declare class DroidFlashlight extends DroidSkill {
842
- protected readonly skillMultiplier: number;
843
- protected readonly strainDecayBase: number;
844
- protected readonly reducedSectionCount: number;
845
- protected readonly reducedSectionBaseline: number;
846
- protected readonly starsPerDouble: number;
895
+ protected readonly strainDecayBase = 0.15;
896
+ protected readonly reducedSectionCount = 0;
897
+ protected readonly reducedSectionBaseline = 1;
898
+ protected readonly starsPerDouble = 1.06;
899
+ private readonly skillMultiplier;
847
900
  private readonly isHidden;
848
901
  private readonly withSliders;
902
+ private currentFlashlightStrain;
849
903
  constructor(mods: Mod[], withSliders: boolean);
850
- /**
851
- * @param current The hitobject to calculate.
852
- */
853
- protected strainValueAt(current: DifficultyHitObject): number;
854
- protected saveToHitObject(current: DifficultyHitObject): void;
904
+ protected strainValueAt(current: DroidDifficultyHitObject): number;
905
+ protected calculateInitialStrain(time: number, current: DifficultyHitObject): number;
906
+ protected getObjectStrain(): number;
907
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
908
+ difficultyValue(): number;
855
909
  }
856
910
 
857
911
  /**
@@ -884,7 +938,7 @@ declare abstract class DroidFlashlightEvaluator extends FlashlightEvaluator {
884
938
  * @param isHiddenMod Whether the Hidden mod is enabled.
885
939
  * @param withSliders Whether to take slider difficulty into account.
886
940
  */
887
- static evaluateDifficultyOf(current: DifficultyHitObject, isHiddenMod: boolean, withSliders: boolean): number;
941
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, isHiddenMod: boolean, withSliders: boolean): number;
888
942
  }
889
943
 
890
944
  /**
@@ -924,7 +978,7 @@ interface PerformanceCalculationOptions {
924
978
  /**
925
979
  * The base class of performance calculators.
926
980
  */
927
- declare abstract class PerformanceCalculator {
981
+ declare abstract class PerformanceCalculator<T extends DifficultyAttributes> {
928
982
  /**
929
983
  * The overall performance value.
930
984
  */
@@ -936,7 +990,7 @@ declare abstract class PerformanceCalculator {
936
990
  /**
937
991
  * The difficulty attributes that is being calculated.
938
992
  */
939
- abstract readonly difficultyAttributes: DifficultyAttributes;
993
+ readonly difficultyAttributes: T;
940
994
  /**
941
995
  * Penalty for combo breaks.
942
996
  */
@@ -959,6 +1013,10 @@ declare abstract class PerformanceCalculator {
959
1013
  * Nerf factor used for nerfing beatmaps with very likely dropped sliderends.
960
1014
  */
961
1015
  protected sliderNerfFactor: number;
1016
+ /**
1017
+ * @param difficultyAttributes The difficulty attributes to calculate.
1018
+ */
1019
+ constructor(difficultyAttributes: T | CacheableDifficultyAttributes<T>);
962
1020
  /**
963
1021
  * Calculates the performance points of the beatmap.
964
1022
  *
@@ -978,7 +1036,7 @@ declare abstract class PerformanceCalculator {
978
1036
  /**
979
1037
  * Calculates the total performance value of the beatmap and stores it in this instance.
980
1038
  */
981
- protected abstract calculateTotalValue(): void;
1039
+ protected abstract calculateTotalValue(): number;
982
1040
  /**
983
1041
  * The total hits that can be done in the beatmap.
984
1042
  */
@@ -1001,12 +1059,19 @@ declare abstract class PerformanceCalculator {
1001
1059
  * Calculates the amount of misses + sliderbreaks from combo.
1002
1060
  */
1003
1061
  private calculateEffectiveMissCount;
1062
+ /**
1063
+ * Determines whether an attribute is a cacheable attribute.
1064
+ *
1065
+ * @param attributes The attributes to check.
1066
+ * @returns Whether the attributes are cacheable.
1067
+ */
1068
+ private isCacheableAttribute;
1004
1069
  }
1005
1070
 
1006
1071
  /**
1007
1072
  * A performance points calculator that calculates performance points for osu!droid gamemode.
1008
1073
  */
1009
- declare class DroidPerformanceCalculator extends PerformanceCalculator {
1074
+ declare class DroidPerformanceCalculator extends PerformanceCalculator<DroidDifficultyAttributes> {
1010
1075
  /**
1011
1076
  * The aim performance value.
1012
1077
  */
@@ -1059,19 +1124,14 @@ declare class DroidPerformanceCalculator extends PerformanceCalculator {
1059
1124
  * Can be properly obtained by analyzing the replay associated with the score.
1060
1125
  */
1061
1126
  get visualSliderCheesePenalty(): number;
1062
- readonly difficultyAttributes: DroidDifficultyAttributes;
1063
1127
  protected finalMultiplier: number;
1064
- protected readonly mode: Modes;
1128
+ protected readonly mode = Modes.droid;
1065
1129
  private _aimSliderCheesePenalty;
1066
1130
  private _flashlightSliderCheesePenalty;
1067
1131
  private _visualSliderCheesePenalty;
1068
1132
  private _tapPenalty;
1069
1133
  private _deviation;
1070
1134
  private _tapDeviation;
1071
- /**
1072
- * @param difficultyAttributes The difficulty attributes to calculate.
1073
- */
1074
- constructor(difficultyAttributes: DroidDifficultyAttributes);
1075
1135
  /**
1076
1136
  * Applies a tap penalty value to this calculator.
1077
1137
  *
@@ -1105,7 +1165,7 @@ declare class DroidPerformanceCalculator extends PerformanceCalculator {
1105
1165
  */
1106
1166
  applyVisualSliderCheesePenalty(value: number): void;
1107
1167
  protected calculateValues(): void;
1108
- protected calculateTotalValue(): void;
1168
+ protected calculateTotalValue(): number;
1109
1169
  protected handleOptions(options?: PerformanceCalculationOptions): void;
1110
1170
  /**
1111
1171
  * Calculates the aim performance value of the beatmap.
@@ -1127,6 +1187,27 @@ declare class DroidPerformanceCalculator extends PerformanceCalculator {
1127
1187
  * Calculates the visual performance value of the beatmap.
1128
1188
  */
1129
1189
  private calculateVisualValue;
1190
+ /**
1191
+ * Calculates a strain-based miss penalty.
1192
+ *
1193
+ * Strain-based miss penalty assumes that a player will miss on the hardest parts of a map,
1194
+ * so we use the amount of relatively difficult sections to adjust miss penalty
1195
+ * to make it more punishing on maps with lower amount of hard sections.
1196
+ */
1197
+ private calculateStrainBasedMissPenalty;
1198
+ /**
1199
+ * The object-based proportional miss penalty.
1200
+ */
1201
+ private get proportionalMissPenalty();
1202
+ /**
1203
+ * Calculates the object-based length scaling based on the deviation of a player for a full
1204
+ * combo in this beatmap, taking retries into account.
1205
+ *
1206
+ * @param objectCount The amount of objects to be considered. Defaults to the amount of
1207
+ * objects in this beatmap.
1208
+ * @param punishForMemorization Whether to punish the deviation for memorization. Defaults to `false`.
1209
+ */
1210
+ private calculateDeviationBasedLengthScaling;
1130
1211
  /**
1131
1212
  * Estimates the player's tap deviation based on the OD, number of circles and sliders,
1132
1213
  * and number of 300s, 100s, 50s, and misses, assuming the player's mean hit error is 0.
@@ -1156,16 +1237,18 @@ declare class DroidPerformanceCalculator extends PerformanceCalculator {
1156
1237
  * Represents the skill required to properly follow a beatmap's rhythm.
1157
1238
  */
1158
1239
  declare class DroidRhythm extends DroidSkill {
1159
- protected readonly skillMultiplier: number;
1160
- protected readonly reducedSectionCount: number;
1161
- protected readonly reducedSectionBaseline: number;
1162
- protected readonly strainDecayBase: number;
1163
- protected readonly starsPerDouble: number;
1164
- private currentRhythm;
1240
+ protected readonly reducedSectionCount = 5;
1241
+ protected readonly reducedSectionBaseline = 0.75;
1242
+ protected readonly strainDecayBase = 0.3;
1243
+ protected readonly starsPerDouble = 1.75;
1244
+ private currentRhythmStrain;
1245
+ private currentRhythmMultiplier;
1165
1246
  private readonly hitWindow;
1166
1247
  constructor(mods: Mod[], overallDifficulty: number);
1167
- protected strainValueAt(current: DifficultyHitObject): number;
1168
- protected saveToHitObject(current: DifficultyHitObject): void;
1248
+ protected strainValueAt(current: DroidDifficultyHitObject): number;
1249
+ protected calculateInitialStrain(time: number, current: DroidDifficultyHitObject): number;
1250
+ protected getObjectStrain(): number;
1251
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
1169
1252
  }
1170
1253
 
1171
1254
  /**
@@ -1189,30 +1272,44 @@ declare abstract class DroidRhythmEvaluator extends RhythmEvaluator {
1189
1272
  * @param current The current object.
1190
1273
  * @param greatWindow The great hit window of the current object.
1191
1274
  */
1192
- static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number): number;
1275
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, greatWindow: number): number;
1193
1276
  }
1194
1277
 
1195
1278
  /**
1196
1279
  * Represents the skill required to press keys or tap with regards to keeping up with the speed at which objects need to be hit.
1197
1280
  */
1198
1281
  declare class DroidTap extends DroidSkill {
1199
- protected readonly skillMultiplier: number;
1200
- protected readonly reducedSectionCount: number;
1201
- protected readonly reducedSectionBaseline: number;
1202
- protected readonly strainDecayBase: number;
1203
- protected readonly starsPerDouble: number;
1282
+ protected readonly reducedSectionCount = 10;
1283
+ protected readonly reducedSectionBaseline = 0.75;
1284
+ protected readonly strainDecayBase = 0.3;
1285
+ protected readonly starsPerDouble = 1.1;
1204
1286
  private currentTapStrain;
1205
- private currentOriginalTapStrain;
1287
+ private currentRhythmMultiplier;
1288
+ private readonly skillMultiplier;
1206
1289
  private readonly greatWindow;
1207
- constructor(mods: Mod[], overallDifficulty: number);
1290
+ private readonly considerCheesability;
1291
+ private readonly strainTimeCap?;
1292
+ private readonly _objectDeltaTimes;
1208
1293
  /**
1209
- * @param current The hitobject to calculate.
1294
+ * The delta time of hitobjects.
1210
1295
  */
1211
- protected strainValueAt(current: DifficultyHitObject): number;
1296
+ get objectDeltaTimes(): readonly number[];
1297
+ constructor(mods: Mod[], overallDifficulty: number, considerCheesability: boolean, strainTimeCap?: number);
1298
+ /**
1299
+ * The amount of notes that are relevant to the difficulty.
1300
+ */
1301
+ relevantNoteCount(): number;
1302
+ /**
1303
+ * The delta time relevant to the difficulty.
1304
+ */
1305
+ relevantDeltaTime(): number;
1306
+ protected strainValueAt(current: DroidDifficultyHitObject): number;
1307
+ protected calculateInitialStrain(time: number, current: DroidDifficultyHitObject): number;
1308
+ protected getObjectStrain(): number;
1212
1309
  /**
1213
1310
  * @param current The hitobject to save to.
1214
1311
  */
1215
- protected saveToHitObject(current: DifficultyHitObject): void;
1312
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
1216
1313
  }
1217
1314
 
1218
1315
  /**
@@ -1233,33 +1330,39 @@ declare abstract class DroidTapEvaluator extends SpeedEvaluator {
1233
1330
  *
1234
1331
  * - time between pressing the previous and current object,
1235
1332
  * - distance between those objects,
1236
- * - and how easily they can be cheesed.
1333
+ * - how easily they can be cheesed,
1334
+ * - and the strain time cap.
1237
1335
  *
1238
1336
  * @param current The current object.
1239
1337
  * @param greatWindow The great hit window of the current object.
1240
1338
  * @param considerCheesability Whether to consider cheesability.
1339
+ * @param strainTimeCap The strain time to cap the object's strain time to.
1241
1340
  */
1242
- static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number, considerCheesability: boolean): number;
1341
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, greatWindow: number, considerCheesability: boolean, strainTimeCap?: number): number;
1243
1342
  }
1244
1343
 
1245
1344
  /**
1246
1345
  * Represents the skill required to read every object in the map.
1247
1346
  */
1248
1347
  declare class DroidVisual extends DroidSkill {
1249
- protected readonly starsPerDouble: number;
1250
- protected readonly reducedSectionCount: number;
1251
- protected readonly reducedSectionBaseline: number;
1252
- protected readonly skillMultiplier: number;
1253
- protected readonly strainDecayBase: number;
1348
+ protected readonly starsPerDouble = 1.025;
1349
+ protected readonly reducedSectionCount = 10;
1350
+ protected readonly reducedSectionBaseline = 0.75;
1351
+ protected readonly strainDecayBase = 0.1;
1254
1352
  private readonly isHidden;
1255
1353
  private readonly withSliders;
1354
+ private currentVisualStrain;
1355
+ private currentRhythmMultiplier;
1356
+ private readonly skillMultiplier;
1256
1357
  constructor(mods: Mod[], withSliders: boolean);
1257
- protected strainValueAt(current: DifficultyHitObject): number;
1258
- protected saveToHitObject(current: DifficultyHitObject): void;
1358
+ protected strainValueAt(current: DroidDifficultyHitObject): number;
1359
+ protected calculateInitialStrain(time: number, current: DroidDifficultyHitObject): number;
1360
+ protected getObjectStrain(): number;
1361
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
1259
1362
  }
1260
1363
 
1261
1364
  /**
1262
- * An evaluator for calculating osu!droid Visual skill.
1365
+ * An evaluator for calculating osu!droid visual skill.
1263
1366
  */
1264
1367
  declare abstract class DroidVisualEvaluator {
1265
1368
  /**
@@ -1277,7 +1380,7 @@ declare abstract class DroidVisualEvaluator {
1277
1380
  * @param isHiddenMod Whether the Hidden mod is enabled.
1278
1381
  * @param withSliders Whether to take slider difficulty into account.
1279
1382
  */
1280
- static evaluateDifficultyOf(current: DifficultyHitObject, isHiddenMod: boolean, withSliders: boolean): number;
1383
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, isHiddenMod: boolean, withSliders: boolean): number;
1281
1384
  }
1282
1385
 
1283
1386
  /**
@@ -1302,6 +1405,72 @@ declare abstract class OsuSkill extends StrainSkill {
1302
1405
  difficultyValue(): number;
1303
1406
  }
1304
1407
 
1408
+ /**
1409
+ * Represents an osu!standard hit object with difficulty calculation values.
1410
+ */
1411
+ declare class OsuDifficultyHitObject extends DifficultyHitObject {
1412
+ /**
1413
+ * The speed strain generated by the hitobject.
1414
+ */
1415
+ speedStrain: number;
1416
+ /**
1417
+ * The flashlight strain generated by this hitobject.
1418
+ */
1419
+ flashlightStrain: number;
1420
+ private readonly radiusBuffThreshold;
1421
+ protected readonly mode = Modes.osu;
1422
+ protected get scalingFactor(): number;
1423
+ /**
1424
+ * Note: You **must** call `computeProperties` at some point due to how TypeScript handles
1425
+ * overridden properties (see [this](https://github.com/microsoft/TypeScript/issues/1617) GitHub issue).
1426
+ *
1427
+ * @param object The underlying hitobject.
1428
+ * @param lastObject The hitobject before this hitobject.
1429
+ * @param lastLastObject The hitobject before the last hitobject.
1430
+ * @param difficultyHitObjects All difficulty hitobjects in the processed beatmap.
1431
+ * @param clockRate The clock rate of the beatmap.
1432
+ */
1433
+ constructor(object: PlaceableHitObject, lastObject: PlaceableHitObject | null, lastLastObject: PlaceableHitObject | null, difficultyHitObjects: readonly DifficultyHitObject[], clockRate: number);
1434
+ }
1435
+
1436
+ /**
1437
+ * Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
1438
+ */
1439
+ declare class OsuAim extends OsuSkill {
1440
+ protected readonly strainDecayBase: number;
1441
+ protected readonly reducedSectionCount: number;
1442
+ protected readonly reducedSectionBaseline: number;
1443
+ protected readonly decayWeight: number;
1444
+ private currentAimStrain;
1445
+ private readonly skillMultiplier;
1446
+ private readonly withSliders;
1447
+ constructor(mods: Mod[], withSliders: boolean);
1448
+ protected strainValueAt(current: OsuDifficultyHitObject): number;
1449
+ protected calculateInitialStrain(time: number, current: OsuDifficultyHitObject): number;
1450
+ /**
1451
+ * @param current The hitobject to save to.
1452
+ */
1453
+ protected saveToHitObject(current: OsuDifficultyHitObject): void;
1454
+ }
1455
+
1456
+ /**
1457
+ * An evaluator for calculating osu!standard Aim skill.
1458
+ */
1459
+ declare abstract class OsuAimEvaluator extends AimEvaluator {
1460
+ /**
1461
+ * Evaluates the difficulty of aiming the current object, based on:
1462
+ *
1463
+ * - cursor velocity to the current object,
1464
+ * - angle difficulty,
1465
+ * - sharp velocity increases,
1466
+ * - and slider difficulty.
1467
+ *
1468
+ * @param current The current object.
1469
+ * @param withSliders Whether to take slider difficulty into account.
1470
+ */
1471
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject, withSliders: boolean): number;
1472
+ }
1473
+
1305
1474
  /**
1306
1475
  * Holds data that can be used to calculate osu!standard performance points.
1307
1476
  */
@@ -1315,22 +1484,23 @@ interface OsuDifficultyAttributes extends DifficultyAttributes {
1315
1484
  /**
1316
1485
  * A difficulty calculator for osu!standard gamemode.
1317
1486
  */
1318
- declare class OsuDifficultyCalculator extends DifficultyCalculator {
1487
+ declare class OsuDifficultyCalculator extends DifficultyCalculator<OsuDifficultyHitObject, OsuDifficultyAttributes> {
1319
1488
  /**
1320
1489
  * The aim star rating of the beatmap.
1321
1490
  */
1322
- aim: number;
1491
+ get aim(): number;
1323
1492
  /**
1324
1493
  * The speed star rating of the beatmap.
1325
1494
  */
1326
- speed: number;
1495
+ get speed(): number;
1327
1496
  /**
1328
1497
  * The flashlight star rating of the beatmap.
1329
1498
  */
1330
- flashlight: number;
1499
+ get flashlight(): number;
1331
1500
  readonly attributes: OsuDifficultyAttributes;
1332
- protected readonly difficultyMultiplier: number;
1333
- protected readonly mode: Modes;
1501
+ get cacheableAttributes(): CacheableDifficultyAttributes<OsuDifficultyAttributes>;
1502
+ protected readonly difficultyMultiplier = 0.0675;
1503
+ protected readonly mode = Modes.osu;
1334
1504
  /**
1335
1505
  * Calculates the aim star rating of the beatmap and stores it in this instance.
1336
1506
  */
@@ -1345,13 +1515,9 @@ declare class OsuDifficultyCalculator extends DifficultyCalculator {
1345
1515
  calculateFlashlight(): void;
1346
1516
  calculateTotal(): void;
1347
1517
  calculateAll(): void;
1348
- /**
1349
- * Returns a string representative of the class.
1350
- */
1351
1518
  toString(): string;
1352
- /**
1353
- * Creates skills to be calculated.
1354
- */
1519
+ protected generateDifficultyHitObjects(convertedBeatmap: Beatmap): OsuDifficultyHitObject[];
1520
+ protected computeDifficultyStatistics(options?: DifficultyCalculationOptions): DifficultyStatisticsCalculatorResult<number, number, number, number>;
1355
1521
  protected createSkills(): OsuSkill[];
1356
1522
  /**
1357
1523
  * Called after aim skill calculation.
@@ -1378,87 +1544,21 @@ declare class OsuDifficultyCalculator extends DifficultyCalculator {
1378
1544
  private postCalculateFlashlight;
1379
1545
  }
1380
1546
 
1381
- /**
1382
- * A difficulty calculator that calculates for both osu!droid and osu!standard gamemode.
1383
- */
1384
- declare class MapStars {
1385
- /**
1386
- * The osu!droid difficulty calculator of the beatmap.
1387
- */
1388
- readonly droid: DroidDifficultyCalculator;
1389
- /**
1390
- * The osu!standard difficulty calculator of the beatmap.
1391
- */
1392
- readonly osu: OsuDifficultyCalculator;
1393
- /**
1394
- * Constructs this instance and calculates the given beatmap's osu!droid and osu!standard difficulty.
1395
- *
1396
- * @param beatmap The beatmap to calculate.
1397
- * @param options Options for the difficulty calculation.
1398
- */
1399
- constructor(beatmap: Beatmap, options?: DifficultyCalculationOptions);
1400
- /**
1401
- * Returns a string representative of the class.
1402
- */
1403
- toString(): string;
1404
- }
1405
-
1406
- /**
1407
- * Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
1408
- */
1409
- declare class OsuAim extends OsuSkill {
1410
- protected readonly skillMultiplier: number;
1411
- protected readonly strainDecayBase: number;
1412
- protected readonly reducedSectionCount: number;
1413
- protected readonly reducedSectionBaseline: number;
1414
- protected readonly difficultyMultiplier: number;
1415
- protected readonly decayWeight: number;
1416
- private readonly withSliders;
1417
- constructor(mods: Mod[], withSliders: boolean);
1418
- /**
1419
- * @param current The hitobject to calculate.
1420
- */
1421
- protected strainValueAt(current: DifficultyHitObject): number;
1422
- /**
1423
- * @param current The hitobject to save to.
1424
- */
1425
- protected saveToHitObject(current: DifficultyHitObject): void;
1426
- }
1427
-
1428
- /**
1429
- * An evaluator for calculating osu!standard Aim skill.
1430
- */
1431
- declare abstract class OsuAimEvaluator extends AimEvaluator {
1432
- /**
1433
- * Evaluates the difficulty of aiming the current object, based on:
1434
- *
1435
- * - cursor velocity to the current object,
1436
- * - angle difficulty,
1437
- * - sharp velocity increases,
1438
- * - and slider difficulty.
1439
- *
1440
- * @param current The current object.
1441
- * @param withSliders Whether to take slider difficulty into account.
1442
- */
1443
- static evaluateDifficultyOf(current: DifficultyHitObject, withSliders: boolean): number;
1444
- }
1445
-
1446
1547
  /**
1447
1548
  * Represents the skill required to memorize and hit every object in a beatmap with the Flashlight mod enabled.
1448
1549
  */
1449
1550
  declare class OsuFlashlight extends OsuSkill {
1450
- protected readonly skillMultiplier: number;
1451
1551
  protected readonly strainDecayBase: number;
1452
1552
  protected readonly reducedSectionCount: number;
1453
1553
  protected readonly reducedSectionBaseline: number;
1454
1554
  protected readonly decayWeight: number;
1555
+ private currentFlashlightStrain;
1556
+ private readonly skillMultiplier;
1455
1557
  private readonly isHidden;
1456
1558
  constructor(mods: Mod[]);
1457
- /**
1458
- * @param current The hitobject to calculate.
1459
- */
1460
- protected strainValueAt(current: DifficultyHitObject): number;
1461
- protected saveToHitObject(current: DifficultyHitObject): void;
1559
+ protected strainValueAt(current: OsuDifficultyHitObject): number;
1560
+ protected calculateInitialStrain(time: number, current: OsuDifficultyHitObject): number;
1561
+ protected saveToHitObject(current: OsuDifficultyHitObject): void;
1462
1562
  }
1463
1563
 
1464
1564
  /**
@@ -1477,13 +1577,13 @@ declare abstract class OsuFlashlightEvaluator extends FlashlightEvaluator {
1477
1577
  * @param current The current object.
1478
1578
  * @param isHiddenMod Whether the Hidden mod is enabled.
1479
1579
  */
1480
- static evaluateDifficultyOf(current: DifficultyHitObject, isHiddenMod: boolean): number;
1580
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject, isHiddenMod: boolean): number;
1481
1581
  }
1482
1582
 
1483
1583
  /**
1484
1584
  * A performance points calculator that calculates performance points for osu!standard gamemode.
1485
1585
  */
1486
- declare class OsuPerformanceCalculator extends PerformanceCalculator {
1586
+ declare class OsuPerformanceCalculator extends PerformanceCalculator<OsuDifficultyAttributes> {
1487
1587
  /**
1488
1588
  * The aim performance value.
1489
1589
  */
@@ -1500,15 +1600,10 @@ declare class OsuPerformanceCalculator extends PerformanceCalculator {
1500
1600
  * The flashlight performance value.
1501
1601
  */
1502
1602
  flashlight: number;
1503
- readonly difficultyAttributes: OsuDifficultyAttributes;
1504
1603
  protected finalMultiplier: number;
1505
1604
  protected readonly mode: Modes;
1506
- /**
1507
- * @param difficultyAttributes The difficulty attributes to calculate.
1508
- */
1509
- constructor(difficultyAttributes: OsuDifficultyAttributes);
1510
1605
  protected calculateValues(): void;
1511
- protected calculateTotalValue(): void;
1606
+ protected calculateTotalValue(): number;
1512
1607
  /**
1513
1608
  * Calculates the aim performance value of the beatmap.
1514
1609
  */
@@ -1539,31 +1634,32 @@ declare abstract class OsuRhythmEvaluator extends RhythmEvaluator {
1539
1634
  * @param current The current object.
1540
1635
  * @param greatWindow The great hit window of the current object.
1541
1636
  */
1542
- static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number): number;
1637
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject, greatWindow: number): number;
1543
1638
  }
1544
1639
 
1545
1640
  /**
1546
1641
  * Represents the skill required to press keys or tap with regards to keeping up with the speed at which objects need to be hit.
1547
1642
  */
1548
1643
  declare class OsuSpeed extends OsuSkill {
1549
- protected readonly skillMultiplier: number;
1550
- protected readonly strainDecayBase: number;
1551
- protected readonly reducedSectionCount: number;
1552
- protected readonly reducedSectionBaseline: number;
1553
- protected readonly difficultyMultiplier: number;
1554
- protected readonly decayWeight: number;
1644
+ protected readonly strainDecayBase = 0.3;
1645
+ protected readonly reducedSectionCount = 5;
1646
+ protected readonly reducedSectionBaseline = 0.75;
1647
+ protected readonly difficultyMultiplier = 1.04;
1648
+ protected readonly decayWeight = 0.9;
1555
1649
  private currentSpeedStrain;
1556
1650
  private currentRhythm;
1651
+ private readonly skillMultiplier;
1557
1652
  private readonly greatWindow;
1558
- constructor(mods: Mod[], greatWindow: number);
1653
+ constructor(mods: Mod[], overallDifficulty: number);
1559
1654
  /**
1560
1655
  * @param current The hitobject to calculate.
1561
1656
  */
1562
- protected strainValueAt(current: DifficultyHitObject): number;
1657
+ protected strainValueAt(current: OsuDifficultyHitObject): number;
1658
+ protected calculateInitialStrain(time: number, current: OsuDifficultyHitObject): number;
1563
1659
  /**
1564
1660
  * @param current The hitobject to save to.
1565
1661
  */
1566
- protected saveToHitObject(current: DifficultyHitObject): void;
1662
+ protected saveToHitObject(current: OsuDifficultyHitObject): void;
1567
1663
  }
1568
1664
 
1569
1665
  /**
@@ -1584,7 +1680,7 @@ declare abstract class OsuSpeedEvaluator extends SpeedEvaluator {
1584
1680
  * @param current The current object.
1585
1681
  * @param greatWindow The great hit window of the current object.
1586
1682
  */
1587
- static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number): number;
1683
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject, greatWindow: number): number;
1588
1684
  }
1589
1685
 
1590
- 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 };
1686
+ 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, RhythmEvaluator, SpeedEvaluator, type StrainPeaks };