@rian8337/osu-difficulty-calculator 4.0.0-beta.8 → 4.0.0-beta.80

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,43 +1,13 @@
1
- import { MapStats, Mod, PlaceableHitObject, Modes, Beatmap, HitObject, Accuracy } from '@rian8337/osu-base';
2
-
3
- /**
4
- * An evaluator for calculating aim skill.
5
- *
6
- * This class should be considered an "evaluating" class and not persisted.
7
- */
8
- declare abstract class AimEvaluator {
9
- protected static readonly wideAngleMultiplier: number;
10
- protected static readonly acuteAngleMultiplier: number;
11
- protected static readonly sliderMultiplier: number;
12
- protected static readonly velocityChangeMultiplier: number;
13
- /**
14
- * Calculates the bonus of wide angles.
15
- */
16
- protected static calculateWideAngleBonus(angle: number): number;
17
- /**
18
- * Calculates the bonus of acute angles.
19
- */
20
- protected static calculateAcuteAngleBonus(angle: number): number;
21
- }
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
- }
1
+ import { ModMap, SerializedMod, PlaceableHitObject, Vector2, Modes, PlayableBeatmap, Mod, Beatmap, DroidPlayableBeatmap, Accuracy, OsuPlayableBeatmap } from '@rian8337/osu-base';
32
2
 
33
3
  /**
34
4
  * Holds data that can be used to calculate performance points.
35
5
  */
36
- interface DifficultyAttributes {
6
+ interface IDifficultyAttributes {
37
7
  /**
38
8
  * The mods which were applied to the beatmap.
39
9
  */
40
- mods: Mod[];
10
+ mods: ModMap;
41
11
  /**
42
12
  * The combined star rating of all skills.
43
13
  */
@@ -72,12 +42,6 @@ interface DifficultyAttributes {
72
42
  * The overall clock rate that was applied to the beatmap.
73
43
  */
74
44
  clockRate: number;
75
- /**
76
- * The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
77
- *
78
- * Rate-adjusting mods don't directly affect the approach rate difficulty value, but have a perceived effect as a result of adjusting audio timing.
79
- */
80
- approachRate: number;
81
45
  /**
82
46
  * The perceived overall difficulty inclusive of rate-adjusting mods (DT/HT/etc), based on osu!standard judgement.
83
47
  *
@@ -96,22 +60,61 @@ interface DifficultyAttributes {
96
60
  * The number of spinners in the beatmap.
97
61
  */
98
62
  spinnerCount: number;
63
+ /**
64
+ * The number of sliders weighted by difficulty.
65
+ */
66
+ aimDifficultSliderCount: number;
67
+ /**
68
+ * The amount of strains that are considered difficult with respect to the aim skill.
69
+ */
70
+ aimDifficultStrainCount: number;
99
71
  }
100
72
 
101
73
  /**
102
- * Represents options for difficulty calculation.
74
+ * Represents difficulty attributes that can be cached.
103
75
  */
104
- interface DifficultyCalculationOptions extends CalculationOptions {
76
+ type CacheableDifficultyAttributes<T extends IDifficultyAttributes> = Omit<T, "mods" | "toCacheableAttributes"> & {
77
+ /**
78
+ * The mods which were applied to the beatmap.
79
+ */
80
+ mods: SerializedMod[];
81
+ };
82
+
83
+ /**
84
+ * Holds data that can be used to calculate performance points.
85
+ */
86
+ declare abstract class DifficultyAttributes implements IDifficultyAttributes {
87
+ mods: ModMap;
88
+ starRating: number;
89
+ maxCombo: number;
90
+ aimDifficulty: number;
91
+ flashlightDifficulty: number;
92
+ speedNoteCount: number;
93
+ sliderFactor: number;
94
+ clockRate: number;
95
+ overallDifficulty: number;
96
+ hitCircleCount: number;
97
+ sliderCount: number;
98
+ spinnerCount: number;
99
+ aimDifficultSliderCount: number;
100
+ aimDifficultStrainCount: number;
101
+ constructor(cacheableAttributes?: CacheableDifficultyAttributes<IDifficultyAttributes>);
105
102
  /**
106
- * The modifications to apply.
103
+ * Converts this `DifficultyAttributes` instance to an attribute structure that can be cached.
104
+ *
105
+ * @returns The cacheable attributes.
107
106
  */
108
- mods?: Mod[];
107
+ toCacheableAttributes(): CacheableDifficultyAttributes<this>;
108
+ /**
109
+ * Returns a string representation of the difficulty attributes.
110
+ */
111
+ toString(): string;
109
112
  }
110
113
 
111
114
  /**
112
- * Represents an osu!standard hit object with difficulty calculation values.
115
+ * Represents a hit object with difficulty calculation values.
113
116
  */
114
- declare class DifficultyHitObject {
117
+ declare abstract class DifficultyHitObject {
115
118
  /**
116
119
  * The underlying hitobject.
117
120
  */
@@ -121,19 +124,7 @@ declare class DifficultyHitObject {
121
124
  *
122
125
  * This is one less than the actual index of the hitobject in the beatmap.
123
126
  */
124
- index: number;
125
- /**
126
- * The preempt time of the hitobject.
127
- */
128
- baseTimePreempt: number;
129
- /**
130
- * Adjusted preempt time of the hitobject, taking speed multiplier into account.
131
- */
132
- timePreempt: number;
133
- /**
134
- * The fade in time of the hitobject.
135
- */
136
- timeFadeIn: number;
127
+ readonly index: number;
137
128
  /**
138
129
  * The aim strain generated by the hitobject if sliders are considered.
139
130
  */
@@ -142,41 +133,10 @@ declare class DifficultyHitObject {
142
133
  * The aim strain generated by the hitobject if sliders are not considered.
143
134
  */
144
135
  aimStrainWithoutSliders: number;
145
- /**
146
- * The tap strain generated by the hitobject.
147
- *
148
- * This is also used for osu!standard as opposed to "speed strain".
149
- */
150
- tapStrain: number;
151
- /**
152
- * The tap strain generated by the hitobject if `strainTime` isn't modified by
153
- * OD. This is used in three-finger detection.
154
- */
155
- originalTapStrain: number;
156
136
  /**
157
137
  * The rhythm multiplier generated by the hitobject. This is used to alter tap strain.
158
138
  */
159
139
  rhythmMultiplier: number;
160
- /**
161
- * The rhythm strain generated by the hitobject.
162
- */
163
- rhythmStrain: number;
164
- /**
165
- * The flashlight strain generated by the hitobject if sliders are considered.
166
- */
167
- flashlightStrainWithSliders: number;
168
- /**
169
- * The flashlight strain generated by the hitobject if sliders are not considered.
170
- */
171
- flashlightStrainWithoutSliders: number;
172
- /**
173
- * The visual strain generated by the hitobject if sliders are considered.
174
- */
175
- visualStrainWithSliders: number;
176
- /**
177
- * The visual strain generated by the hitobject if sliders are not considered.
178
- */
179
- visualStrainWithoutSliders: number;
180
140
  /**
181
141
  * The normalized distance from the "lazy" end position of the previous hitobject to the start position of this hitobject.
182
142
  *
@@ -208,6 +168,21 @@ declare class DifficultyHitObject {
208
168
  * The time taken to travel through `travelDistance`, with a minimum value of 25ms for sliders.
209
169
  */
210
170
  travelTime: number;
171
+ /**
172
+ * The position of the cursor at the point of completion of this hitobject if it was hit
173
+ * with as few movements as possible.
174
+ */
175
+ lazyEndPosition?: Vector2;
176
+ /**
177
+ * The distance travelled by the cursor upon completion of this hitobject if it was hit
178
+ * with as few movements as possible.
179
+ */
180
+ lazyTravelDistance: number;
181
+ /**
182
+ * The time taken by the cursor upon completion of this hitobject if it was hit with
183
+ * as few movements as possible.
184
+ */
185
+ lazyTravelTime: number;
211
186
  /**
212
187
  * Angle the player has to take to hit this hitobject.
213
188
  *
@@ -217,42 +192,66 @@ declare class DifficultyHitObject {
217
192
  /**
218
193
  * The amount of milliseconds elapsed between this hitobject and the last hitobject.
219
194
  */
220
- deltaTime: number;
195
+ readonly deltaTime: number;
221
196
  /**
222
197
  * The amount of milliseconds elapsed since the start time of the previous hitobject, with a minimum of 25ms.
223
198
  */
224
- strainTime: number;
199
+ readonly strainTime: number;
225
200
  /**
226
201
  * Adjusted start time of the hitobject, taking speed multiplier into account.
227
202
  */
228
- startTime: number;
203
+ readonly startTime: number;
229
204
  /**
230
205
  * Adjusted end time of the hitobject, taking speed multiplier into account.
231
206
  */
232
- endTime: number;
207
+ readonly endTime: number;
233
208
  /**
234
- * The note density of the hitobject.
209
+ * The full great window of the hitobject.
235
210
  */
236
- noteDensity: number;
211
+ readonly fullGreatWindow: number;
237
212
  /**
238
- * The overlapping factor of the hitobject.
239
- *
240
- * This is used to scale visual skill.
213
+ * Selective bonus for beatmaps with higher circle size.
241
214
  */
242
- overlappingFactor: number;
215
+ abstract get smallCircleBonus(): number;
243
216
  /**
244
- * Adjusted velocity of the hitobject, taking speed multiplier into account.
217
+ * Other hitobjects in the beatmap, including this hitobject.
245
218
  */
246
- velocity: number;
219
+ protected readonly hitObjects: readonly DifficultyHitObject[];
247
220
  /**
248
- * Other hitobjects in the beatmap, including this hitobject.
221
+ * The normalized radius of the hitobject.
249
222
  */
250
- private readonly hitObjects;
223
+ static readonly normalizedRadius: number;
224
+ /**
225
+ * The normalized diameter of the hitobject.
226
+ */
227
+ static get normalizedDiameter(): number;
228
+ protected abstract readonly mode: Modes;
229
+ protected readonly maximumSliderRadius: number;
230
+ protected readonly assumedSliderRadius: number;
231
+ /**
232
+ * The lowest possible delta time value.
233
+ */
234
+ static readonly minDeltaTime = 25;
235
+ private readonly lastObject;
236
+ private readonly lastDifficultyObject;
237
+ private readonly lastLastDifficultyObject;
251
238
  /**
239
+ * Note: You **must** call `computeProperties` at some point due to how TypeScript handles
240
+ * overridden properties (see [this](https://github.com/microsoft/TypeScript/issues/1617) GitHub issue).
241
+ *
252
242
  * @param object The underlying hitobject.
253
- * @param hitObjects All difficulty hitobjects in the processed beatmap.
243
+ * @param lastObject The hitobject before this hitobject.
244
+ * @param difficultyHitObjects All difficulty hitobjects in the processed beatmap.
245
+ * @param clockRate The clock rate of the beatmap.
246
+ * @param index The index of this hitobject in the list of all hitobjects.
254
247
  */
255
- constructor(object: PlaceableHitObject, hitObjects: DifficultyHitObject[]);
248
+ constructor(object: PlaceableHitObject, lastObject: PlaceableHitObject | null, difficultyHitObjects: readonly DifficultyHitObject[], clockRate: number, index: number);
249
+ /**
250
+ * Computes the properties of this hitobject.
251
+ *
252
+ * @param clockRate The clock rate of the beatmap.
253
+ */
254
+ computeProperties(clockRate: number): void;
256
255
  /**
257
256
  * Gets the difficulty hitobject at a specific index with respect to the current
258
257
  * difficulty hitobject's index.
@@ -263,7 +262,7 @@ declare class DifficultyHitObject {
263
262
  * @returns The difficulty hitobject at the index with respect to the current
264
263
  * difficulty hitobject's index, `null` if the index is out of range.
265
264
  */
266
- previous(backwardsIndex: number): DifficultyHitObject | null;
265
+ previous(backwardsIndex: number): this | null;
267
266
  /**
268
267
  * Gets the difficulty hitobject at a specific index with respect to the current
269
268
  * difficulty hitobject's index.
@@ -274,26 +273,25 @@ declare class DifficultyHitObject {
274
273
  * @returns The difficulty hitobject at the index with respect to the current
275
274
  * difficulty hitobject's index, `null` if the index is out of range.
276
275
  */
277
- next(forwardsIndex: number): DifficultyHitObject | null;
276
+ next(forwardsIndex: number): this | null;
278
277
  /**
279
278
  * Calculates the opacity of the hitobject at a given time.
280
279
  *
281
280
  * @param time The time to calculate the hitobject's opacity at.
282
- * @param isHidden Whether Hidden mod is used.
283
- * @param mode The gamemode to calculate the opacity for.
281
+ * @param mods The mods used.
284
282
  * @returns The opacity of the hitobject at the given time.
285
283
  */
286
- opacityAt(time: number, isHidden: boolean, mode: Modes): number;
284
+ opacityAt(time: number, mods: ModMap): number;
287
285
  /**
288
- * Determines whether this hitobject is considered overlapping with the hitobject before it.
289
- *
290
- * Keep in mind that "overlapping" in this case is overlapping to the point where both hitobjects
291
- * can be hit with just a single tap in osu!droid.
286
+ * How possible is it to doubletap this object together with the next one and get perfect
287
+ * judgement in range from 0 to 1.
292
288
  *
293
- * @param considerDistance Whether to consider the distance between both hitobjects.
294
- * @returns Whether the hitobject is considered overlapping.
289
+ * A value closer to 1 indicates a higher possibility.
295
290
  */
296
- isOverlapping(considerDistance: boolean): boolean;
291
+ get doubletapness(): number;
292
+ protected setDistances(clockRate: number): void;
293
+ private calculateSliderCursorPosition;
294
+ private getEndCursorPosition;
297
295
  }
298
296
 
299
297
  /**
@@ -305,8 +303,8 @@ declare abstract class Skill {
305
303
  /**
306
304
  * The mods that this skill processes.
307
305
  */
308
- protected readonly mods: Mod[];
309
- constructor(mods: Mod[]);
306
+ protected readonly mods: ModMap;
307
+ constructor(mods: ModMap);
310
308
  /**
311
309
  * Processes a hitobject.
312
310
  *
@@ -320,18 +318,32 @@ declare abstract class Skill {
320
318
  }
321
319
 
322
320
  /**
323
- * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
324
- * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
321
+ * Represents the strain peaks of various calculated difficulties.
325
322
  */
326
- declare abstract class StrainSkill extends Skill {
323
+ interface StrainPeaks {
327
324
  /**
328
- * The strain of currently calculated hitobject.
325
+ * The strain peaks of aim difficulty if sliders are considered.
329
326
  */
330
- protected currentStrain: number;
327
+ aimWithSliders: number[];
331
328
  /**
332
- * The current section's strain peak.
329
+ * The strain peaks of aim difficulty if sliders are not considered.
333
330
  */
334
- protected currentSectionPeak: number;
331
+ aimWithoutSliders: number[];
332
+ /**
333
+ * The strain peaks of speed difficulty.
334
+ */
335
+ speed: number[];
336
+ /**
337
+ * The strain peaks of flashlight difficulty.
338
+ */
339
+ flashlight: number[];
340
+ }
341
+
342
+ /**
343
+ * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
344
+ * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
345
+ */
346
+ declare abstract class StrainSkill extends Skill {
335
347
  /**
336
348
  * Strain peaks are stored here.
337
349
  */
@@ -345,247 +357,265 @@ declare abstract class StrainSkill extends Skill {
345
357
  * The baseline multiplier applied to the section with the biggest strain.
346
358
  */
347
359
  protected abstract readonly reducedSectionBaseline: number;
348
- /**
349
- * Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other.
350
- */
351
- protected abstract readonly skillMultiplier: number;
352
360
  /**
353
361
  * Determines how quickly strain decays for the given skill.
354
362
  *
355
363
  * For example, a value of 0.15 indicates that strain decays to 15% of its original value in one second.
356
364
  */
357
365
  protected abstract readonly strainDecayBase: number;
358
- private readonly sectionLength;
359
- private currentSectionEnd;
360
- private isFirstObject;
366
+ protected readonly _objectStrains: number[];
367
+ protected difficulty: number;
361
368
  /**
362
- * Calculates the strain value of a hitobject and stores the value in it. This value is affected by previously processed objects.
363
- *
364
- * @param current The hitobject to process.
369
+ * The strains of hitobjects.
365
370
  */
371
+ get objectStrains(): readonly number[];
372
+ private readonly sectionLength;
373
+ private currentStrain;
374
+ private currentSectionPeak;
375
+ private currentSectionEnd;
366
376
  process(current: DifficultyHitObject): void;
367
377
  /**
368
378
  * Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty.
369
379
  */
370
380
  saveCurrentPeak(): void;
381
+ /**
382
+ * Returns the number of strains weighed against the top strain.
383
+ *
384
+ * The result is scaled by clock rate as it affects the total number of strains.
385
+ */
386
+ countTopWeightedStrains(): number;
371
387
  /**
372
388
  * Calculates strain decay for a specified time frame.
373
389
  *
374
390
  * @param ms The time frame to calculate.
375
391
  */
376
392
  protected strainDecay(ms: number): number;
393
+ /**
394
+ * Calculates the starting time of a strain section at an object.
395
+ *
396
+ * @param current The object at which the strain section starts.
397
+ * @returns The start time of the strain section.
398
+ */
399
+ protected calculateCurrentSectionStart(current: DifficultyHitObject): number;
377
400
  /**
378
401
  * Calculates the strain value at a hitobject.
402
+ *
403
+ * @param current The hitobject to calculate.
379
404
  */
380
405
  protected abstract strainValueAt(current: DifficultyHitObject): number;
381
406
  /**
382
407
  * Saves the current strain to a hitobject.
383
408
  */
384
409
  protected abstract saveToHitObject(current: DifficultyHitObject): void;
410
+ /**
411
+ * Retrieves the peak strain at a point in time.
412
+ *
413
+ * @param time The time to retrieve the peak strain at.
414
+ * @param current The current hit object.
415
+ * @returns The peak strain.
416
+ */
417
+ protected abstract calculateInitialStrain(time: number, current: DifficultyHitObject): number;
385
418
  /**
386
419
  * Sets the initial strain level for a new section.
387
420
  *
388
- * @param offset The beginning of the new section in milliseconds, adjusted by speed multiplier.
421
+ * @param time The beginning of the new section in milliseconds.
389
422
  * @param current The current hitobject.
390
423
  */
391
424
  private startNewSectionFrom;
392
425
  }
393
426
 
394
427
  /**
395
- * Represents the strain peaks of various calculated difficulties.
428
+ * The base of a difficulty calculator.
396
429
  */
397
- interface StrainPeaks {
398
- /**
399
- * The strain peaks of aim difficulty if sliders are considered.
400
- */
401
- aimWithSliders: number[];
402
- /**
403
- * The strain peaks of aim difficulty if sliders are not considered.
404
- */
405
- aimWithoutSliders: number[];
406
- /**
407
- * The strain peaks of speed difficulty.
408
- */
409
- speed: number[];
410
- /**
411
- * The strain peaks of flashlight difficulty.
412
- */
413
- flashlight: number[];
414
- }
415
-
416
- /**
417
- * The base of difficulty calculators.
418
- */
419
- declare abstract class DifficultyCalculator {
420
- /**
421
- * The calculated beatmap.
422
- */
423
- readonly beatmap: Beatmap;
424
- /**
425
- * The difficulty objects of the beatmap.
426
- */
427
- readonly objects: DifficultyHitObject[];
428
- /**
429
- * The modifications applied.
430
- */
431
- mods: Mod[];
430
+ declare abstract class DifficultyCalculator<TBeatmap extends PlayableBeatmap, THitObject extends DifficultyHitObject, TAttributes extends DifficultyAttributes> {
431
+ protected abstract readonly difficultyMultiplier: number;
432
432
  /**
433
- * The total star rating of the beatmap.
433
+ * `Mod`s that adjust the difficulty of a beatmap.
434
434
  */
435
- total: number;
435
+ protected readonly difficultyAdjustmentMods: (typeof Mod)[];
436
436
  /**
437
- * The map statistics of the beatmap after modifications are applied.
437
+ * Retains `Mod`s that adjust a beatmap's difficulty from the specified mods.
438
+ *
439
+ * @param mods The mods to retain the difficulty adjustment mods from.
440
+ * @returns The retained difficulty adjustment mods.
438
441
  */
439
- stats: MapStats;
442
+ abstract retainDifficultyAdjustmentMods(mods: Mod[]): Mod[];
440
443
  /**
441
- * The strain peaks of various calculated difficulties.
444
+ * Calculates the difficulty of a `PlayableBeatmap`.
445
+ *
446
+ * @param beatmap The `PlayableBeatmap` whose difficulty is to be calculated.
447
+ * @returns A `DifficultyAttributes` object describing the difficulty of the `Beatmap`.
442
448
  */
443
- readonly strainPeaks: StrainPeaks;
449
+ calculate(beatmap: TBeatmap): TAttributes;
444
450
  /**
445
- * Holds data that can be used to calculate performance points.
451
+ * Calculates the difficulty of a `Beatmap` with specific `Mod`s.
452
+ *
453
+ * @param beatmap The `Beatmap` whose difficulty is to be calculated.
454
+ * @param mods The `Mod`s to apply to the beatmap. Defaults to No Mod.
455
+ * @returns A `DifficultyAttributes` object describing the difficulty of the `Beatmap`.
446
456
  */
447
- abstract readonly attributes: DifficultyAttributes;
448
- protected abstract readonly difficultyMultiplier: number;
449
- protected abstract readonly mode: Modes;
457
+ calculate(beatmap: Beatmap, mods?: ModMap): TAttributes;
450
458
  /**
451
- * Constructs a new instance of the calculator.
459
+ * Obtains the strain peaks of a `PlayableBeatmap`.
452
460
  *
453
- * @param beatmap The beatmap to calculate. This beatmap will be deep-cloned to prevent reference changes.
461
+ * @param beatmap The `PlayableBeatmap` whose strain peaks are to be calculated.
462
+ * @returns The strain peaks of the `PlayableBeatmap`.
454
463
  */
455
- constructor(beatmap: Beatmap);
464
+ calculateStrainPeaks(beatmap: TBeatmap): StrainPeaks;
456
465
  /**
457
- * Calculates the star rating of the specified beatmap.
458
- *
459
- * The beatmap is analyzed in chunks of `sectionLength` duration.
460
- * For each chunk the highest hitobject strains are added to
461
- * a list which is then collapsed into a weighted sum, much
462
- * like scores are weighted on a user's profile.
466
+ * Obtains the strain peaks of a `Beatmap` with specific `Mod`s.
463
467
  *
464
- * For subsequent chunks, the initial max strain is calculated
465
- * by decaying the previous hitobject's strain until the
466
- * beginning of the new chunk.
467
- *
468
- * @param options Options for the difficulty calculation.
469
- * @returns The current instance.
468
+ * @param beatmap The `Beatmap` whose strain peaks are to be calculated.
469
+ * @param mods The `Mod`s to apply to the beatmap. Defaults to No Mod.
470
+ * @returns The strain peaks of the `Beatmap`.
470
471
  */
471
- calculate(options?: DifficultyCalculationOptions): this;
472
+ calculateStrainPeaks(beatmap: Beatmap, mods?: ModMap): StrainPeaks;
472
473
  /**
473
- * Generates difficulty hitobjects for this calculator.
474
+ * Creates the `Skill`s to calculate the difficulty of a `PlayableBeatmap`.
475
+ *
476
+ * @param beatmap The `PlayableBeatmap` whose difficulty will be calculated.
477
+ * @return The `Skill`s.
474
478
  */
475
- generateDifficultyHitObjects(): void;
479
+ protected abstract createSkills(beatmap: TBeatmap): Skill[];
476
480
  /**
477
- * Performs some pre-processing before proceeding with difficulty calculation.
481
+ * Creates the `Skill`s to obtain the strain peaks of a `PlayableBeatmap`.
482
+ *
483
+ * @param beatmap
478
484
  */
479
- protected preProcess(): void;
485
+ protected abstract createStrainPeakSkills(beatmap: TBeatmap): StrainSkill[];
480
486
  /**
481
- * Calculates the skills provided.
487
+ * Creates difficulty hitobjects for this calculator.
482
488
  *
483
- * @param skills The skills to calculate.
489
+ * @param beatmap The beatmap to generate difficulty hitobjects from.
490
+ * @returns The generated difficulty hitobjects.
484
491
  */
485
- protected calculateSkills(...skills: StrainSkill[]): void;
492
+ protected abstract createDifficultyHitObjects(beatmap: TBeatmap): THitObject[];
486
493
  /**
487
- * Calculates the total star rating of the beatmap and stores it in this instance.
494
+ * Creates a `DifficultyAttributes` object to describe a `PlayableBeatmap`'s difficulty.
495
+ *
496
+ * @param beatmap The `PlayableBeatmap` whose difficulty was calculated.
497
+ * @param skills The `Skill`s which processed the `PlayableBeatmap`.
498
+ * @param objects The `DifficultyHitObject`s which were processed.
499
+ * @returns The `DifficultyAttributes` object.
488
500
  */
489
- abstract calculateTotal(): void;
501
+ protected abstract createDifficultyAttributes(beatmap: TBeatmap, skills: Skill[], objects: THitObject[]): TAttributes;
490
502
  /**
491
- * Calculates every star rating of the beatmap and stores it in this instance.
503
+ * Constructs a `PlayableBeatmap` from a `Beatmap` with specific `Mod`s.
504
+ *
505
+ * @param beatmap The `Beatmap` to create a `PlayableBeatmap` from.
506
+ * @param mods The `Mod`s to apply to the `Beatmap`.
507
+ * @returns The `PlayableBeatmap`.
492
508
  */
493
- abstract calculateAll(): void;
509
+ protected abstract createPlayableBeatmap(beatmap: Beatmap, mods?: ModMap): TBeatmap;
494
510
  /**
495
- * Returns a string representative of the class.
511
+ * Calculates the base rating of a `Skill`.
512
+ *
513
+ * @param skill The `Skill` to calculate the rating of.
514
+ * @returns The rating of the `Skill`.
496
515
  */
497
- abstract toString(): string;
516
+ protected calculateRating(skill: Skill): number;
498
517
  /**
499
- * Creates skills to be calculated.
518
+ * Calculates the base performance value of a difficulty rating.
519
+ *
520
+ * @param rating The difficulty rating.
500
521
  */
501
- protected abstract createSkills(): StrainSkill[];
522
+ protected basePerformanceValue(rating: number): number;
523
+ private createPlayableBeatmapWithDifficultyAdjustmentMods;
524
+ }
525
+
526
+ /**
527
+ * Represents a slider that is considered difficult.
528
+ *
529
+ * This structure is a part of difficulty attributes and can be cached.
530
+ */
531
+ interface DifficultSlider {
502
532
  /**
503
- * Populates the stored difficulty attributes with necessary data.
533
+ * The index of the slider in the beatmap.
504
534
  */
505
- protected populateDifficultyAttributes(): void;
535
+ readonly index: number;
506
536
  /**
507
- * Calculates the star rating value of a difficulty.
537
+ * The difficulty rating of this slider compared to other sliders, based on the velocity of the slider.
508
538
  *
509
- * @param difficulty The difficulty to calculate.
510
- */
511
- protected starValue(difficulty: number): number;
512
- /**
513
- * Calculates the base performance value of a difficulty rating.
539
+ * A value closer to 1 indicates that this slider is more difficult compared to most sliders.
514
540
  *
515
- * @param rating The difficulty rating.
541
+ * A value closer to 0 indicates that this slider is easier compared to most sliders.
516
542
  */
517
- protected basePerformanceValue(rating: number): number;
543
+ readonly difficultyRating: number;
518
544
  }
519
545
 
520
546
  /**
521
- * A converter used to convert normal hitobjects into difficulty hitobjects.
547
+ * Represents an osu!droid hit object with difficulty calculation values.
522
548
  */
523
- declare class DifficultyHitObjectCreator {
549
+ declare class DroidDifficultyHitObject extends DifficultyHitObject {
524
550
  /**
525
- * The threshold for small circle buff for osu!droid.
551
+ * The tap strain generated by the hitobject.
526
552
  */
527
- private readonly DROID_CIRCLESIZE_BUFF_THRESHOLD;
553
+ tapStrain: number;
528
554
  /**
529
- * The threshold for small circle buff for osu!standard.
555
+ * The tap strain generated by the hitobject if `strainTime` isn't modified by
556
+ * OD. This is used in three-finger detection.
530
557
  */
531
- private readonly PC_CIRCLESIZE_BUFF_THRESHOLD;
558
+ originalTapStrain: number;
532
559
  /**
533
- * The gamemode this creator is creating for.
560
+ * The rhythm strain generated by the hitobject.
534
561
  */
535
- private mode;
562
+ rhythmStrain: number;
536
563
  /**
537
- * The base normalized radius of hitobjects.
564
+ * The flashlight strain generated by the hitobject if sliders are considered.
538
565
  */
539
- private readonly normalizedRadius;
540
- private maximumSliderRadius;
541
- private readonly assumedSliderRadius;
542
- private readonly minDeltaTime;
566
+ flashlightStrainWithSliders: number;
543
567
  /**
544
- * Generates difficulty hitobjects for difficulty calculation.
568
+ * The flashlight strain generated by the hitobject if sliders are not considered.
545
569
  */
546
- generateDifficultyObjects(params: {
547
- objects: readonly HitObject[];
548
- circleSize: number;
549
- mods: Mod[];
550
- speedMultiplier: number;
551
- mode: Modes;
552
- preempt?: number;
553
- }): DifficultyHitObject[];
570
+ flashlightStrainWithoutSliders: number;
554
571
  /**
555
- * Calculates a slider's cursor position.
572
+ * The reading difficulty generated by the hitobject.
556
573
  */
557
- private calculateSliderCursorPosition;
574
+ readingDifficulty: number;
558
575
  /**
559
- * Gets the scaling factor of a radius.
576
+ * The note density of the hitobject.
577
+ */
578
+ noteDensity: number;
579
+ /**
580
+ * The overlapping factor of the hitobject.
560
581
  *
561
- * @param radius The radius to get the scaling factor from.
582
+ * This is used to scale visual skill.
562
583
  */
563
- private getScalingFactor;
584
+ overlappingFactor: number;
564
585
  /**
565
- * Returns the end cursor position of a hitobject.
586
+ * Adjusted preempt time of the hitobject, taking speed multiplier into account.
566
587
  */
567
- private getEndCursorPosition;
568
- private applyToOverlappingFactor;
569
- }
570
-
571
- /**
572
- * Represents a slider that is considered difficult.
573
- *
574
- * This structure is a part of difficulty attributes and can be cached.
575
- */
576
- interface DifficultSlider {
588
+ readonly timePreempt: number;
589
+ private readonly radiusBuffThreshold;
590
+ protected readonly mode = Modes.droid;
591
+ protected readonly maximumSliderRadius: number;
592
+ get smallCircleBonus(): number;
577
593
  /**
578
- * 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 difficultyHitObjects All difficulty hitobjects in the processed beatmap.
600
+ * @param clockRate The clock rate of the beatmap.
579
601
  */
580
- readonly index: number;
602
+ constructor(object: PlaceableHitObject, lastObject: PlaceableHitObject | null, difficultyHitObjects: readonly DifficultyHitObject[], clockRate: number, index: number);
603
+ opacityAt(time: number, mods: ModMap): number;
604
+ previous(backwardsIndex: number): this | null;
605
+ next(forwardsIndex: number): this | null;
581
606
  /**
582
- * 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.
583
608
  *
584
- * 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.
585
611
  *
586
- * A value closer to 0 indicates that this slider is easier compared to most sliders.
612
+ * In the case of sliders, it is considered overlapping if all nested hitobjects can be hit with
613
+ * one aim motion.
614
+ *
615
+ * @param considerDistance Whether to consider the distance between both hitobjects.
616
+ * @returns Whether the hitobject is considered overlapping.
587
617
  */
588
- readonly difficultyRating: number;
618
+ isOverlapping(considerDistance: boolean): boolean;
589
619
  }
590
620
 
591
621
  /**
@@ -597,41 +627,55 @@ declare abstract class DroidSkill extends StrainSkill {
597
627
  * The bonus multiplier that is given for a sequence of notes of equal difficulty.
598
628
  */
599
629
  protected abstract readonly starsPerDouble: number;
630
+ process(current: DifficultyHitObject): void;
600
631
  difficultyValue(): number;
632
+ /**
633
+ * Gets the strain of a hitobject.
634
+ *
635
+ * @param current The hitobject to get the strain from.
636
+ * @returns The strain of the hitobject.
637
+ */
638
+ protected abstract getObjectStrain(current: DifficultyHitObject): number;
639
+ protected calculateCurrentSectionStart(current: DifficultyHitObject): number;
601
640
  }
602
641
 
603
642
  /**
604
643
  * Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
605
644
  */
606
645
  declare class DroidAim extends DroidSkill {
607
- protected readonly skillMultiplier: number;
608
- protected readonly strainDecayBase: number;
609
- protected readonly reducedSectionCount: number;
610
- protected readonly reducedSectionBaseline: number;
611
- protected readonly starsPerDouble: number;
612
- private readonly withSliders;
613
- constructor(mods: Mod[], withSliders: boolean);
614
- /**
615
- * @param current The hitobject to calculate.
616
- */
617
- protected strainValueAt(current: DifficultyHitObject): number;
646
+ protected readonly strainDecayBase = 0.15;
647
+ protected readonly reducedSectionCount = 10;
648
+ protected readonly reducedSectionBaseline = 0.75;
649
+ protected readonly starsPerDouble = 1.05;
650
+ private readonly skillMultiplier;
651
+ private currentAimStrain;
652
+ private readonly sliderStrains;
653
+ private maxSliderStrain;
654
+ readonly withSliders: boolean;
655
+ constructor(mods: ModMap, withSliders: boolean);
656
+ /**
657
+ * Obtains the amount of sliders that are considered difficult in terms of relative strain.
658
+ */
659
+ countDifficultSliders(): number;
660
+ protected strainValueAt(current: DroidDifficultyHitObject): number;
661
+ protected calculateInitialStrain(time: number, current: DroidDifficultyHitObject): number;
662
+ protected getObjectStrain(): number;
618
663
  /**
619
664
  * @param current The hitobject to save to.
620
665
  */
621
- protected saveToHitObject(current: DifficultyHitObject): void;
666
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
622
667
  }
623
668
 
624
669
  /**
625
670
  * An evaluator for calculating osu!droid Aim skill.
626
671
  */
627
- declare abstract class DroidAimEvaluator extends AimEvaluator {
628
- protected static readonly wideAngleMultiplier: number;
629
- protected static readonly sliderMultiplier: number;
630
- protected static readonly velocityChangeMultiplier: number;
631
- /**
632
- * Spacing threshold for a single hitobject spacing.
633
- */
634
- private static readonly SINGLE_SPACING_THRESHOLD;
672
+ declare abstract class DroidAimEvaluator {
673
+ private static readonly wideAngleMultiplier;
674
+ private static readonly acuteAngleMultiplier;
675
+ private static readonly sliderMultiplier;
676
+ private static readonly velocityChangeMultiplier;
677
+ private static readonly wiggleMultiplier;
678
+ private static readonly singleSpacingThreshold;
635
679
  private static readonly minSpeedBonus;
636
680
  /**
637
681
  * Evaluates the difficulty of aiming the current object, based on:
@@ -644,21 +688,23 @@ declare abstract class DroidAimEvaluator extends AimEvaluator {
644
688
  * @param current The current object.
645
689
  * @param withSliders Whether to take slider difficulty into account.
646
690
  */
647
- static evaluateDifficultyOf(current: DifficultyHitObject, withSliders: boolean): number;
691
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, withSliders: boolean): number;
648
692
  /**
649
- * Calculates the aim strain of a hitobject.
693
+ * Calculates the snap aim strain of a hitobject.
650
694
  */
651
- private static aimStrainOf;
695
+ private static snapAimStrainOf;
652
696
  /**
653
- * Calculates the movement strain of a hitobject.
697
+ * Calculates the flow aim strain of a hitobject.
654
698
  */
655
- private static movementStrainOf;
699
+ private static flowAimStrainOf;
700
+ private static calculateWideAngleBonus;
701
+ private static calculateAcuteAngleBonus;
656
702
  }
657
703
 
658
704
  /**
659
705
  * Holds data that can be used to calculate osu!droid performance points.
660
706
  */
661
- interface DroidDifficultyAttributes extends DifficultyAttributes {
707
+ interface IDroidDifficultyAttributes extends IDifficultyAttributes {
662
708
  /**
663
709
  * The difficulty corresponding to the tap skill.
664
710
  */
@@ -668,211 +714,163 @@ interface DroidDifficultyAttributes extends DifficultyAttributes {
668
714
  */
669
715
  rhythmDifficulty: number;
670
716
  /**
671
- * The difficulty corresponding to the visual skill.
717
+ * The difficulty corresponding to the reading skill.
672
718
  */
673
- visualDifficulty: number;
674
- }
675
-
676
- /**
677
- * Represents a beatmap section at which the strains of objects are considerably high.
678
- */
679
- interface HighStrainSection {
680
- /**
681
- * The index of the first object in this section with respect to the full beatmap.
682
- */
683
- readonly firstObjectIndex: number;
684
- /**
685
- * The index of the last object in this section with respect to the full beatmap.
686
- */
687
- readonly lastObjectIndex: number;
688
- /**
689
- * The summed strain of this section.
690
- */
691
- readonly sumStrain: number;
692
- }
693
-
694
- /**
695
- * Holds data that can be used to calculate osu!droid performance points as well
696
- * as doing some analysis using the replay of a score.
697
- */
698
- interface ExtendedDroidDifficultyAttributes extends DroidDifficultyAttributes {
719
+ readingDifficulty: number;
699
720
  /**
700
- * Possible sections at which the player can use three fingers on.
721
+ * The amount of strains that are considered difficult with respect to the tap skill.
701
722
  */
702
- possibleThreeFingeredSections: HighStrainSection[];
723
+ tapDifficultStrainCount: number;
703
724
  /**
704
- * Sliders that are considered difficult.
725
+ * The amount of strains that are considered difficult with respect to the flashlight skill.
705
726
  */
706
- difficultSliders: DifficultSlider[];
727
+ flashlightDifficultStrainCount: number;
707
728
  /**
708
- * The number of clickable objects weighted by difficulty.
709
- *
710
- * Related to aim difficulty.
729
+ * The amount of notes that are considered difficult with respect to the reading skill.
711
730
  */
712
- aimNoteCount: number;
731
+ readingDifficultNoteCount: number;
713
732
  /**
714
- * Describes how much of flashlight difficulty is contributed to by hitcircles or sliders.
715
- *
716
- * A value closer to 1 indicates most of flashlight difficulty is contributed by hitcircles.
717
- *
718
- * A value closer to 0 indicates most of flashlight difficulty is contributed by sliders.
733
+ * The average delta time of speed objects.
719
734
  */
720
- flashlightSliderFactor: number;
735
+ averageSpeedDeltaTime: number;
721
736
  /**
722
- * Describes how much of visual difficulty is contributed to by hitcircles or sliders.
737
+ * Describes how much of tap difficulty is contributed by notes that are "vibroable".
723
738
  *
724
- * A value closer to 1 indicates most of visual difficulty is contributed by hitcircles.
739
+ * A value closer to 1 indicates most of tap difficulty is contributed by notes that are not "vibroable".
725
740
  *
726
- * A value closer to 0 indicates most of visual difficulty is contributed by sliders.
741
+ * A value closer to 0 indicates most of tap difficulty is contributed by notes that are "vibroable".
727
742
  */
728
- visualSliderFactor: number;
743
+ vibroFactor: number;
729
744
  }
730
745
 
731
746
  /**
732
- * A difficulty calculator for osu!droid gamemode.
733
- */
734
- declare class DroidDifficultyCalculator extends DifficultyCalculator {
735
- /**
736
- * The aim star rating of the beatmap.
737
- */
738
- aim: number;
739
- /**
740
- * The tap star rating of the beatmap.
741
- */
742
- tap: number;
743
- /**
744
- * The rhythm star rating of the beatmap.
745
- */
746
- rhythm: number;
747
- /**
748
- * The flashlight star rating of the beatmap.
749
- */
750
- flashlight: number;
751
- /**
752
- * The visual star rating of the beatmap.
753
- */
754
- visual: number;
755
- /**
756
- * The strain threshold to start detecting for possible three-fingered section.
757
- *
758
- * Increasing this number will result in less sections being flagged.
759
- */
760
- static readonly threeFingerStrainThreshold: number;
761
- readonly attributes: ExtendedDroidDifficultyAttributes;
762
- protected readonly difficultyMultiplier: number;
763
- protected readonly mode: Modes;
764
- /**
765
- * Calculates the aim star rating of the beatmap and stores it in this instance.
766
- */
767
- calculateAim(): void;
768
- /**
769
- * Calculates the tap star rating of the beatmap and stores it in this instance.
770
- */
771
- calculateTap(): void;
772
- /**
773
- * Calculates the rhythm star rating of the beatmap and stores it in this instance.
774
- */
775
- calculateRhythm(): void;
776
- /**
777
- * Calculates the flashlight star rating of the beatmap and stores it in this instance.
778
- */
779
- calculateFlashlight(): void;
780
- /**
781
- * Calculates the visual star rating of the beatmap and stores it in this instance.
782
- */
783
- calculateVisual(): void;
784
- calculateTotal(): void;
785
- calculateAll(): void;
747
+ * Holds data that can be used to calculate osu!droid performance points.
748
+ */
749
+ declare class DroidDifficultyAttributes extends DifficultyAttributes implements IDroidDifficultyAttributes {
750
+ tapDifficulty: number;
751
+ rhythmDifficulty: number;
752
+ readingDifficulty: number;
753
+ tapDifficultStrainCount: number;
754
+ flashlightDifficultStrainCount: number;
755
+ readingDifficultNoteCount: number;
756
+ averageSpeedDeltaTime: number;
757
+ vibroFactor: number;
758
+ constructor(cacheableAttributes?: CacheableDifficultyAttributes<IDroidDifficultyAttributes>);
786
759
  toString(): string;
787
- protected preProcess(): void;
788
- protected createSkills(): DroidSkill[];
760
+ }
761
+
762
+ /**
763
+ * Represents a beatmap section at which the strains of objects are considerably high.
764
+ */
765
+ interface HighStrainSection {
789
766
  /**
790
- * Called after aim skill calculation.
791
- *
792
- * @param aimSkill The aim skill that considers sliders.
793
- * @param aimSkillWithoutSliders The aim skill that doesn't consider sliders.
767
+ * The index of the first object in this section with respect to the full beatmap.
794
768
  */
795
- private postCalculateAim;
769
+ readonly firstObjectIndex: number;
796
770
  /**
797
- * Calculates aim-related attributes.
771
+ * The index of the last object in this section with respect to the full beatmap.
798
772
  */
799
- private calculateAimAttributes;
773
+ readonly lastObjectIndex: number;
800
774
  /**
801
- * Called after tap skill calculation.
802
- *
803
- * @param tapSkill The tap skill.
775
+ * The summed strain of this section.
804
776
  */
805
- private postCalculateTap;
777
+ readonly sumStrain: number;
778
+ }
779
+
780
+ /**
781
+ * Holds data that can be used to calculate osu!droid performance points as well
782
+ * as doing some analysis using the replay of a score.
783
+ */
784
+ interface IExtendedDroidDifficultyAttributes extends IDroidDifficultyAttributes {
806
785
  /**
807
- * Calculates speed-related attributes.
786
+ * The mode of the difficulty calculation.
808
787
  */
809
- private calculateSpeedAttributes;
788
+ mode: "live";
810
789
  /**
811
- * Calculates the sum of strains for possible three-fingered sections.
812
- *
813
- * @param firstObjectIndex The index of the first object in the section.
814
- * @param lastObjectIndex The index of the last object in the section.
815
- * @returns The summed strain of the section.
790
+ * Possible sections at which the player can use three fingers on.
816
791
  */
817
- private calculateThreeFingerSummedStrain;
792
+ possibleThreeFingeredSections: HighStrainSection[];
818
793
  /**
819
- * Called after rhythm skill calculation.
820
- *
821
- * @param rhythmSkill The rhythm skill.
794
+ * Sliders that are considered difficult.
822
795
  */
823
- private postCalculateRhythm;
796
+ difficultSliders: DifficultSlider[];
824
797
  /**
825
- * Called after flashlight skill calculation.
798
+ * Describes how much of flashlight difficulty is contributed to by hitcircles or sliders.
826
799
  *
827
- * @param flashlightSkill The flashlight skill that considers sliders.
828
- * @param flashlightSkillWithoutSliders The flashlight skill that doesn't consider sliders.
829
- */
830
- private postCalculateFlashlight;
831
- /**
832
- * Called after visual skill calculation.
800
+ * A value closer to 1 indicates most of flashlight difficulty is contributed by hitcircles.
833
801
  *
834
- * @param visualSkillWithSliders The visual skill that considers sliders.
835
- * @param visualSkillWithoutSliders The visual skill that doesn't consider sliders.
802
+ * A value closer to 0 indicates most of flashlight difficulty is contributed by sliders.
836
803
  */
837
- private postCalculateVisual;
804
+ flashlightSliderFactor: number;
838
805
  }
839
806
 
840
807
  /**
841
- * Represents the skill required to memorize and hit every object in a beatmap with the Flashlight mod enabled.
808
+ * Holds data that can be used to calculate osu!droid performance points as well
809
+ * as doing some analysis using the replay of a score.
842
810
  */
843
- declare class DroidFlashlight extends DroidSkill {
844
- protected readonly skillMultiplier: number;
845
- protected readonly strainDecayBase: number;
846
- protected readonly reducedSectionCount: number;
847
- protected readonly reducedSectionBaseline: number;
848
- protected readonly starsPerDouble: number;
849
- private readonly isHidden;
850
- private readonly withSliders;
851
- constructor(mods: Mod[], withSliders: boolean);
811
+ declare class ExtendedDroidDifficultyAttributes extends DroidDifficultyAttributes implements IExtendedDroidDifficultyAttributes {
812
+ mode: "live";
813
+ possibleThreeFingeredSections: HighStrainSection[];
814
+ difficultSliders: DifficultSlider[];
815
+ flashlightSliderFactor: number;
816
+ constructor(cacheableAttributes?: CacheableDifficultyAttributes<IExtendedDroidDifficultyAttributes>);
817
+ }
818
+
819
+ /**
820
+ * A difficulty calculator for osu!droid gamemode.
821
+ */
822
+ declare class DroidDifficultyCalculator extends DifficultyCalculator<DroidPlayableBeatmap, DroidDifficultyHitObject, ExtendedDroidDifficultyAttributes> {
852
823
  /**
853
- * @param current The hitobject to calculate.
824
+ * The strain threshold to start detecting for possible three-fingered section.
825
+ *
826
+ * Increasing this number will result in less sections being flagged.
854
827
  */
855
- protected strainValueAt(current: DifficultyHitObject): number;
856
- protected saveToHitObject(current: DifficultyHitObject): void;
828
+ static readonly threeFingerStrainThreshold = 175;
829
+ protected readonly difficultyMultiplier = 0.18;
830
+ constructor();
831
+ retainDifficultyAdjustmentMods(mods: Mod[]): Mod[];
832
+ protected createDifficultyAttributes(beatmap: DroidPlayableBeatmap, skills: Skill[], objects: DroidDifficultyHitObject[]): ExtendedDroidDifficultyAttributes;
833
+ protected createPlayableBeatmap(beatmap: Beatmap, mods?: ModMap): DroidPlayableBeatmap;
834
+ protected createDifficultyHitObjects(beatmap: DroidPlayableBeatmap): DroidDifficultyHitObject[];
835
+ protected createSkills(beatmap: DroidPlayableBeatmap): Skill[];
836
+ protected createStrainPeakSkills(beatmap: DroidPlayableBeatmap): StrainSkill[];
837
+ private populateAimAttributes;
838
+ private populateTapAttributes;
839
+ private populateRhythmAttributes;
840
+ private populateFlashlightAttributes;
841
+ private populateReadingAttributes;
857
842
  }
858
843
 
859
844
  /**
860
- * An evaluator for calculating flashlight skill.
861
- *
862
- * This class should be considered an "evaluating" class and not persisted.
845
+ * Represents the skill required to memorize and hit every object in a beatmap with the Flashlight mod enabled.
863
846
  */
864
- declare abstract class FlashlightEvaluator {
865
- protected static readonly maxOpacityBonus: number;
866
- protected static readonly hiddenBonus: number;
867
- protected static readonly minVelocity: number;
868
- protected static readonly sliderMultiplier: number;
869
- protected static readonly minAngleMultiplier: number;
847
+ declare class DroidFlashlight extends DroidSkill {
848
+ protected readonly strainDecayBase = 0.15;
849
+ protected readonly reducedSectionCount = 0;
850
+ protected readonly reducedSectionBaseline = 1;
851
+ protected readonly starsPerDouble = 1.06;
852
+ private readonly skillMultiplier;
853
+ private currentFlashlightStrain;
854
+ readonly withSliders: boolean;
855
+ constructor(mods: ModMap, withSliders: boolean);
856
+ protected strainValueAt(current: DroidDifficultyHitObject): number;
857
+ protected calculateInitialStrain(time: number, current: DifficultyHitObject): number;
858
+ protected getObjectStrain(): number;
859
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
860
+ difficultyValue(): number;
870
861
  }
871
862
 
872
863
  /**
873
864
  * An evaluator for calculating osu!droid Flashlight skill.
874
865
  */
875
- declare abstract class DroidFlashlightEvaluator extends FlashlightEvaluator {
866
+ declare abstract class DroidFlashlightEvaluator {
867
+ private static readonly maxOpacityBonus;
868
+ private static readonly hiddenBonus;
869
+ private static readonly traceableCircleBonus;
870
+ private static readonly traceableObjectBonus;
871
+ private static readonly minVelocity;
872
+ private static readonly sliderMultiplier;
873
+ private static readonly minAngleMultiplier;
876
874
  /**
877
875
  * Evaluates the difficulty of memorizing and hitting the current object, based on:
878
876
  *
@@ -883,10 +881,10 @@ declare abstract class DroidFlashlightEvaluator extends FlashlightEvaluator {
883
881
  * - and whether Hidden mod is enabled.
884
882
  *
885
883
  * @param current The current object.
886
- * @param isHiddenMod Whether the Hidden mod is enabled.
884
+ * @param mods The mods used.
887
885
  * @param withSliders Whether to take slider difficulty into account.
888
886
  */
889
- static evaluateDifficultyOf(current: DifficultyHitObject, isHiddenMod: boolean, withSliders: boolean): number;
887
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, mods: ModMap, withSliders: boolean): number;
890
888
  }
891
889
 
892
890
  /**
@@ -905,6 +903,14 @@ interface PerformanceCalculationOptions {
905
903
  * The amount of misses achieved in the score.
906
904
  */
907
905
  miss?: number;
906
+ /**
907
+ * The amount of slider ends dropped in the score.
908
+ */
909
+ sliderEndsDropped?: number;
910
+ /**
911
+ * The amount of slider ticks missed in the score.
912
+ */
913
+ sliderTicksMissed?: number;
908
914
  /**
909
915
  * The tap penalty to apply for penalized scores. Only used when using `DroidPerformanceCalculator`.
910
916
  */
@@ -926,7 +932,7 @@ interface PerformanceCalculationOptions {
926
932
  /**
927
933
  * The base class of performance calculators.
928
934
  */
929
- declare abstract class PerformanceCalculator {
935
+ declare abstract class PerformanceCalculator<T extends IDifficultyAttributes> {
930
936
  /**
931
937
  * The overall performance value.
932
938
  */
@@ -938,11 +944,11 @@ declare abstract class PerformanceCalculator {
938
944
  /**
939
945
  * The difficulty attributes that is being calculated.
940
946
  */
941
- abstract readonly difficultyAttributes: DifficultyAttributes;
947
+ readonly difficultyAttributes: T | CacheableDifficultyAttributes<T>;
942
948
  /**
943
- * Penalty for combo breaks.
949
+ * The mods that were used.
944
950
  */
945
- protected comboPenalty: number;
951
+ protected readonly mods: ModMap;
946
952
  /**
947
953
  * The global multiplier to be applied to the final performance value.
948
954
  *
@@ -957,10 +963,29 @@ declare abstract class PerformanceCalculator {
957
963
  * The amount of misses that are filtered out from sliderbreaks.
958
964
  */
959
965
  protected effectiveMissCount: number;
966
+ /**
967
+ * The amount of slider ends dropped in the score.
968
+ */
969
+ protected sliderEndsDropped: number;
970
+ /**
971
+ * The amount of slider ticks missed in the score.
972
+ *
973
+ * This is used to calculate the slider accuracy.
974
+ */
975
+ protected sliderTicksMissed: number;
976
+ private _usingClassicSliderAccuracy;
977
+ /**
978
+ * Whether this score uses classic slider accuracy.
979
+ */
980
+ protected get usingClassicSliderAccuracy(): boolean;
960
981
  /**
961
982
  * Nerf factor used for nerfing beatmaps with very likely dropped sliderends.
962
983
  */
963
984
  protected sliderNerfFactor: number;
985
+ /**
986
+ * @param difficultyAttributes The difficulty attributes to calculate.
987
+ */
988
+ constructor(difficultyAttributes: T | CacheableDifficultyAttributes<T>);
964
989
  /**
965
990
  * Calculates the performance points of the beatmap.
966
991
  *
@@ -980,7 +1005,7 @@ declare abstract class PerformanceCalculator {
980
1005
  /**
981
1006
  * Calculates the total performance value of the beatmap and stores it in this instance.
982
1007
  */
983
- protected abstract calculateTotalValue(): void;
1008
+ protected abstract calculateTotalValue(): number;
984
1009
  /**
985
1010
  * The total hits that can be done in the beatmap.
986
1011
  */
@@ -989,6 +1014,10 @@ declare abstract class PerformanceCalculator {
989
1014
  * The total hits that were successfully done.
990
1015
  */
991
1016
  protected get totalSuccessfulHits(): number;
1017
+ /**
1018
+ * The total of imperfect hits (100s, 50s, misses).
1019
+ */
1020
+ protected get totalImperfectHits(): number;
992
1021
  /**
993
1022
  * Calculates the base performance value of a star rating.
994
1023
  */
@@ -999,16 +1028,31 @@ declare abstract class PerformanceCalculator {
999
1028
  * @param options Options for performance calculation.
1000
1029
  */
1001
1030
  protected handleOptions(options?: PerformanceCalculationOptions): void;
1031
+ /**
1032
+ * Calculates a strain-based miss penalty.
1033
+ *
1034
+ * Strain-based miss penalty assumes that a player will miss on the hardest parts of a map,
1035
+ * so we use the amount of relatively difficult sections to adjust miss penalty
1036
+ * to make it more punishing on maps with lower amount of hard sections.
1037
+ */
1038
+ protected calculateStrainBasedMissPenalty(difficultStrainCount: number): number;
1002
1039
  /**
1003
1040
  * Calculates the amount of misses + sliderbreaks from combo.
1004
1041
  */
1005
1042
  private calculateEffectiveMissCount;
1043
+ /**
1044
+ * Determines whether an attribute is a cacheable attribute.
1045
+ *
1046
+ * @param attributes The attributes to check.
1047
+ * @returns Whether the attributes are cacheable.
1048
+ */
1049
+ private isCacheableAttribute;
1006
1050
  }
1007
1051
 
1008
1052
  /**
1009
1053
  * A performance points calculator that calculates performance points for osu!droid gamemode.
1010
1054
  */
1011
- declare class DroidPerformanceCalculator extends PerformanceCalculator {
1055
+ declare class DroidPerformanceCalculator extends PerformanceCalculator<IDroidDifficultyAttributes> {
1012
1056
  /**
1013
1057
  * The aim performance value.
1014
1058
  */
@@ -1026,9 +1070,9 @@ declare class DroidPerformanceCalculator extends PerformanceCalculator {
1026
1070
  */
1027
1071
  flashlight: number;
1028
1072
  /**
1029
- * The visual performance value.
1073
+ * The reading performance value.
1030
1074
  */
1031
- visual: number;
1075
+ reading: number;
1032
1076
  /**
1033
1077
  * The penalty used to penalize the tap performance value.
1034
1078
  *
@@ -1055,25 +1099,13 @@ declare class DroidPerformanceCalculator extends PerformanceCalculator {
1055
1099
  * Can be properly obtained by analyzing the replay associated with the score.
1056
1100
  */
1057
1101
  get flashlightSliderCheesePenalty(): number;
1058
- /**
1059
- * The penalty used to penalize the visual performance value.
1060
- *
1061
- * Can be properly obtained by analyzing the replay associated with the score.
1062
- */
1063
- get visualSliderCheesePenalty(): number;
1064
- readonly difficultyAttributes: DroidDifficultyAttributes;
1065
1102
  protected finalMultiplier: number;
1066
- protected readonly mode: Modes;
1103
+ protected readonly mode = Modes.droid;
1067
1104
  private _aimSliderCheesePenalty;
1068
1105
  private _flashlightSliderCheesePenalty;
1069
- private _visualSliderCheesePenalty;
1070
1106
  private _tapPenalty;
1071
1107
  private _deviation;
1072
1108
  private _tapDeviation;
1073
- /**
1074
- * @param difficultyAttributes The difficulty attributes to calculate.
1075
- */
1076
- constructor(difficultyAttributes: DroidDifficultyAttributes);
1077
1109
  /**
1078
1110
  * Applies a tap penalty value to this calculator.
1079
1111
  *
@@ -1098,16 +1130,8 @@ declare class DroidPerformanceCalculator extends PerformanceCalculator {
1098
1130
  * @param value The slider cheese penalty value. Must be between 0 and 1.
1099
1131
  */
1100
1132
  applyFlashlightSliderCheesePenalty(value: number): void;
1101
- /**
1102
- * Applies a visual slider cheese penalty value to this calculator.
1103
- *
1104
- * The visual and total performance value will be recalculated afterwards.
1105
- *
1106
- * @param value The slider cheese penalty value. Must be between 0 and 1.
1107
- */
1108
- applyVisualSliderCheesePenalty(value: number): void;
1109
1133
  protected calculateValues(): void;
1110
- protected calculateTotalValue(): void;
1134
+ protected calculateTotalValue(): number;
1111
1135
  protected handleOptions(options?: PerformanceCalculationOptions): void;
1112
1136
  /**
1113
1137
  * Calculates the aim performance value of the beatmap.
@@ -1126,11 +1150,24 @@ declare class DroidPerformanceCalculator extends PerformanceCalculator {
1126
1150
  */
1127
1151
  private calculateFlashlightValue;
1128
1152
  /**
1129
- * Calculates the visual performance value of the beatmap.
1153
+ * Calculates the reading performance value of the beatmap.
1154
+ */
1155
+ private calculateReadingValue;
1156
+ /**
1157
+ * The object-based proportional miss penalty.
1130
1158
  */
1131
- private calculateVisualValue;
1159
+ private get proportionalMissPenalty();
1132
1160
  /**
1133
- * Estimates the player's tap deviation based on the OD, number of circles and sliders,
1161
+ * Calculates the object-based length scaling based on the deviation of a player for a full
1162
+ * combo in this beatmap, taking retries into account.
1163
+ *
1164
+ * @param objectCount The amount of objects to be considered. Defaults to the amount of
1165
+ * objects in this beatmap.
1166
+ * @param punishForMemorization Whether to punish the deviation for memorization. Defaults to `false`.
1167
+ */
1168
+ private calculateDeviationBasedLengthScaling;
1169
+ /**
1170
+ * Estimates the player's deviation based on the OD, number of circles and sliders,
1134
1171
  * and number of 300s, 100s, 50s, and misses, assuming the player's mean hit error is 0.
1135
1172
  *
1136
1173
  * The estimation is consistent in that two SS scores on the same map
@@ -1151,281 +1188,255 @@ declare class DroidPerformanceCalculator extends PerformanceCalculator {
1151
1188
  * This is fine though, since this method is only used to scale tap pp.
1152
1189
  */
1153
1190
  private calculateTapDeviation;
1191
+ /**
1192
+ * Calculates a multiplier for tap to account for improper tapping based on the deviation and tap difficulty.
1193
+ *
1194
+ * [Graph](https://www.desmos.com/calculator/z5l9ebrwpi)
1195
+ */
1196
+ private calculateTapHighDeviationNerf;
1197
+ private getConvertedHitWindow;
1154
1198
  toString(): string;
1155
1199
  }
1156
1200
 
1157
1201
  /**
1158
- * Represents the skill required to properly follow a beatmap's rhythm.
1202
+ * Represents the skill required to read every object in the map.
1159
1203
  */
1160
- declare class DroidRhythm extends DroidSkill {
1161
- protected readonly skillMultiplier: number;
1162
- protected readonly reducedSectionCount: number;
1163
- protected readonly reducedSectionBaseline: number;
1164
- protected readonly strainDecayBase: number;
1165
- protected readonly starsPerDouble: number;
1166
- private currentRhythm;
1167
- private readonly hitWindow;
1168
- constructor(mods: Mod[], overallDifficulty: number);
1169
- protected strainValueAt(current: DifficultyHitObject): number;
1170
- protected saveToHitObject(current: DifficultyHitObject): void;
1204
+ declare class DroidReading extends Skill {
1205
+ private readonly clockRate;
1206
+ private readonly hitObjects;
1207
+ private readonly noteDifficulties;
1208
+ private readonly strainDecayBase;
1209
+ private readonly skillMultiplier;
1210
+ private currentNoteDifficulty;
1211
+ private difficulty;
1212
+ private noteWeightSum;
1213
+ constructor(mods: ModMap, clockRate: number, hitObjects: readonly PlaceableHitObject[]);
1214
+ process(current: DroidDifficultyHitObject): void;
1215
+ difficultyValue(): number;
1216
+ /**
1217
+ * Returns the number of relevant objects weighted against the top note.
1218
+ */
1219
+ countTopWeightedNotes(): number;
1220
+ private strainDecay;
1171
1221
  }
1172
1222
 
1173
1223
  /**
1174
- * An evaluator for calculating rhythm skill.
1175
- *
1176
- * This class should be considered an "evaluating" class and not persisted.
1224
+ * An evaluator for calculating osu!droid reading skill.
1225
+ */
1226
+ declare abstract class DroidReadingEvaluator {
1227
+ private static readonly emptyModMap;
1228
+ private static readonly readingWindowSize;
1229
+ private static readonly distanceInfluenceThreshold;
1230
+ private static readonly hiddenMultiplier;
1231
+ private static readonly densityMultiplier;
1232
+ private static readonly densityDifficultyBase;
1233
+ private static readonly preemptBalancingFactor;
1234
+ private static readonly preemptStartingPoint;
1235
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, clockRate: number, mods: ModMap): number;
1236
+ /**
1237
+ * Retrieves a list of objects that are visible at the point in time the current object needs to be hit.
1238
+ *
1239
+ * @param current The current object.
1240
+ */
1241
+ private static retrievePastVisibleObjects;
1242
+ /**
1243
+ * Calculates the density of objects visible at the point in time the current object needs to be hit.
1244
+ *
1245
+ * @param current The current object.
1246
+ */
1247
+ private static calculateCurrentVisibleObjectsDensity;
1248
+ /**
1249
+ * Returns the time an object spends invisible with the Hidden mod at the current approach rate.
1250
+ *
1251
+ * @param current The current object.
1252
+ */
1253
+ private static getDurationSpentInvisible;
1254
+ /**
1255
+ * Calculates a factor of how often the current object's angle has been repeated in a certain time frame.
1256
+ * It does this by checking the difference in angle between current and past objects and sums them up
1257
+ * based on a range of similarity.
1258
+ *
1259
+ * @param current The current object.
1260
+ */
1261
+ private static getConstantAngleNerfFactor;
1262
+ private static getTimeNerfFactor;
1263
+ }
1264
+
1265
+ /**
1266
+ * Represents the skill required to properly follow a beatmap's rhythm.
1177
1267
  */
1178
- declare abstract class RhythmEvaluator {
1179
- protected static readonly rhythmMultiplier: number;
1180
- protected static readonly historyTimeMax: number;
1268
+ declare class DroidRhythm extends DroidSkill {
1269
+ protected readonly reducedSectionCount = 5;
1270
+ protected readonly reducedSectionBaseline = 0.75;
1271
+ protected readonly strainDecayBase = 0.3;
1272
+ protected readonly starsPerDouble = 1.75;
1273
+ private readonly useSliderAccuracy;
1274
+ private currentRhythmStrain;
1275
+ private currentRhythmMultiplier;
1276
+ constructor(mods: ModMap);
1277
+ protected strainValueAt(current: DroidDifficultyHitObject): number;
1278
+ protected calculateInitialStrain(time: number, current: DroidDifficultyHitObject): number;
1279
+ protected getObjectStrain(): number;
1280
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
1181
1281
  }
1182
1282
 
1183
1283
  /**
1184
1284
  * An evaluator for calculating osu!droid Rhythm skill.
1185
1285
  */
1186
- declare abstract class DroidRhythmEvaluator extends RhythmEvaluator {
1286
+ declare abstract class DroidRhythmEvaluator {
1287
+ private static readonly historyTimeMax;
1288
+ private static readonly historyObjectsMax;
1289
+ private static readonly rhythmOverallMultiplier;
1290
+ private static readonly rhythmRatioMultiplier;
1187
1291
  /**
1188
1292
  * Calculates a rhythm multiplier for the difficulty of the tap associated
1189
1293
  * with historic data of the current object.
1190
1294
  *
1191
1295
  * @param current The current object.
1192
- * @param greatWindow The great hit window of the current object.
1296
+ * @param useSliderAccuracy Whether to use slider accuracy.
1193
1297
  */
1194
- static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number): number;
1298
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, useSliderAccuracy: boolean): number;
1195
1299
  }
1196
1300
 
1197
1301
  /**
1198
1302
  * Represents the skill required to press keys or tap with regards to keeping up with the speed at which objects need to be hit.
1199
1303
  */
1200
1304
  declare class DroidTap extends DroidSkill {
1201
- protected readonly skillMultiplier: number;
1202
- protected readonly reducedSectionCount: number;
1203
- protected readonly reducedSectionBaseline: number;
1204
- protected readonly strainDecayBase: number;
1205
- protected readonly starsPerDouble: number;
1305
+ protected readonly reducedSectionCount = 10;
1306
+ protected readonly reducedSectionBaseline = 0.75;
1307
+ protected readonly strainDecayBase = 0.3;
1308
+ protected readonly starsPerDouble = 1.1;
1206
1309
  private currentTapStrain;
1207
- private currentOriginalTapStrain;
1208
- private readonly greatWindow;
1209
- constructor(mods: Mod[], overallDifficulty: number);
1310
+ private currentRhythmMultiplier;
1311
+ private readonly skillMultiplier;
1312
+ private readonly _objectDeltaTimes;
1313
+ private maxStrain;
1210
1314
  /**
1211
- * @param current The hitobject to calculate.
1315
+ * The delta time of hitobjects.
1316
+ */
1317
+ get objectDeltaTimes(): readonly number[];
1318
+ readonly considerCheesability: boolean;
1319
+ readonly strainTimeCap?: number;
1320
+ constructor(mods: ModMap, considerCheesability: boolean, strainTimeCap?: number);
1321
+ /**
1322
+ * The amount of notes that are relevant to the difficulty.
1323
+ */
1324
+ relevantNoteCount(): number;
1325
+ /**
1326
+ * The delta time relevant to the difficulty.
1212
1327
  */
1213
- protected strainValueAt(current: DifficultyHitObject): number;
1328
+ relevantDeltaTime(): number;
1329
+ protected strainValueAt(current: DroidDifficultyHitObject): number;
1330
+ protected calculateInitialStrain(time: number, current: DroidDifficultyHitObject): number;
1331
+ protected getObjectStrain(): number;
1214
1332
  /**
1215
1333
  * @param current The hitobject to save to.
1216
1334
  */
1217
- protected saveToHitObject(current: DifficultyHitObject): void;
1218
- }
1219
-
1220
- /**
1221
- * An evaluator for calculating speed or tap skill.
1222
- *
1223
- * This class should be considered an "evaluating" class and not persisted.
1224
- */
1225
- declare abstract class SpeedEvaluator {
1226
- protected static readonly minSpeedBonus: number;
1335
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
1227
1336
  }
1228
1337
 
1229
1338
  /**
1230
1339
  * An evaluator for calculating osu!droid tap skill.
1231
1340
  */
1232
- declare abstract class DroidTapEvaluator extends SpeedEvaluator {
1341
+ declare abstract class DroidTapEvaluator {
1342
+ private static readonly minSpeedBonus;
1233
1343
  /**
1234
1344
  * Evaluates the difficulty of tapping the current object, based on:
1235
1345
  *
1236
1346
  * - time between pressing the previous and current object,
1237
1347
  * - distance between those objects,
1238
- * - and how easily they can be cheesed.
1348
+ * - how easily they can be cheesed,
1349
+ * - and the strain time cap.
1239
1350
  *
1240
1351
  * @param current The current object.
1241
- * @param greatWindow The great hit window of the current object.
1242
1352
  * @param considerCheesability Whether to consider cheesability.
1353
+ * @param strainTimeCap The strain time to cap the object's strain time to.
1243
1354
  */
1244
- static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number, considerCheesability: boolean): number;
1245
- }
1246
-
1247
- /**
1248
- * Represents the skill required to read every object in the map.
1249
- */
1250
- declare class DroidVisual extends DroidSkill {
1251
- protected readonly starsPerDouble: number;
1252
- protected readonly reducedSectionCount: number;
1253
- protected readonly reducedSectionBaseline: number;
1254
- protected readonly skillMultiplier: number;
1255
- protected readonly strainDecayBase: number;
1256
- private readonly isHidden;
1257
- private readonly withSliders;
1258
- constructor(mods: Mod[], withSliders: boolean);
1259
- protected strainValueAt(current: DifficultyHitObject): number;
1260
- protected saveToHitObject(current: DifficultyHitObject): void;
1261
- }
1262
-
1263
- /**
1264
- * An evaluator for calculating osu!droid Visual skill.
1265
- */
1266
- declare abstract class DroidVisualEvaluator {
1267
- /**
1268
- * Evaluates the difficulty of reading the current object, based on:
1269
- *
1270
- * - note density of the current object,
1271
- * - overlapping factor of the current object,
1272
- * - the preempt time of the current object,
1273
- * - the visual opacity of the current object,
1274
- * - the velocity of the current object if it's a slider,
1275
- * - past objects' velocity if they are sliders,
1276
- * - and whether the Hidden mod is enabled.
1277
- *
1278
- * @param current The current object.
1279
- * @param isHiddenMod Whether the Hidden mod is enabled.
1280
- * @param withSliders Whether to take slider difficulty into account.
1281
- */
1282
- static evaluateDifficultyOf(current: DifficultyHitObject, isHiddenMod: boolean, withSliders: boolean): number;
1355
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, considerCheesability: boolean, strainTimeCap?: number): number;
1283
1356
  }
1284
1357
 
1285
1358
  /**
1286
- * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
1287
- * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
1359
+ * Holds data that can be used to calculate osu!standard performance points.
1288
1360
  */
1289
- declare abstract class OsuSkill extends StrainSkill {
1361
+ interface IOsuDifficultyAttributes extends IDifficultyAttributes {
1290
1362
  /**
1291
- * The default multiplier applied to the final difficulty value after all other calculations.
1363
+ * The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
1292
1364
  *
1293
- * May be overridden via {@link difficultyMultiplier}.
1294
- */
1295
- static readonly defaultDifficultyMultiplier: number;
1296
- /**
1297
- * The final multiplier to be applied to the final difficulty value after all other calculations.
1298
- */
1299
- protected readonly difficultyMultiplier: number;
1300
- /**
1301
- * The weight by which each strain value decays.
1365
+ * Rate-adjusting mods don't directly affect the approach rate difficulty value, but have a perceived effect as a result of adjusting audio timing.
1302
1366
  */
1303
- protected abstract readonly decayWeight: number;
1304
- difficultyValue(): number;
1305
- }
1306
-
1307
- /**
1308
- * Holds data that can be used to calculate osu!standard performance points.
1309
- */
1310
- interface OsuDifficultyAttributes extends DifficultyAttributes {
1367
+ approachRate: number;
1311
1368
  /**
1312
1369
  * The difficulty corresponding to the speed skill.
1313
1370
  */
1314
1371
  speedDifficulty: number;
1372
+ /**
1373
+ * The amount of strains that are considered difficult with respect to the speed skill.
1374
+ */
1375
+ speedDifficultStrainCount: number;
1315
1376
  }
1316
1377
 
1317
1378
  /**
1318
- * A difficulty calculator for osu!standard gamemode.
1379
+ * Represents an osu!standard hit object with difficulty calculation values.
1319
1380
  */
1320
- declare class OsuDifficultyCalculator extends DifficultyCalculator {
1321
- /**
1322
- * The aim star rating of the beatmap.
1323
- */
1324
- aim: number;
1325
- /**
1326
- * The speed star rating of the beatmap.
1327
- */
1328
- speed: number;
1329
- /**
1330
- * The flashlight star rating of the beatmap.
1331
- */
1332
- flashlight: number;
1333
- readonly attributes: OsuDifficultyAttributes;
1334
- protected readonly difficultyMultiplier: number;
1335
- protected readonly mode: Modes;
1336
- /**
1337
- * Calculates the aim star rating of the beatmap and stores it in this instance.
1338
- */
1339
- calculateAim(): void;
1340
- /**
1341
- * Calculates the speed star rating of the beatmap and stores it in this instance.
1342
- */
1343
- calculateSpeed(): void;
1381
+ declare class OsuDifficultyHitObject extends DifficultyHitObject {
1344
1382
  /**
1345
- * Calculates the flashlight star rating of the beatmap and stores it in this instance.
1383
+ * The speed strain generated by the hitobject.
1346
1384
  */
1347
- calculateFlashlight(): void;
1348
- calculateTotal(): void;
1349
- calculateAll(): void;
1350
- toString(): string;
1351
- protected preProcess(): void;
1352
- protected createSkills(): OsuSkill[];
1353
- /**
1354
- * Called after aim skill calculation.
1355
- *
1356
- * @param aimSkill The aim skill that considers sliders.
1357
- * @param aimSkillWithoutSliders The aim skill that doesn't consider sliders.
1358
- */
1359
- private postCalculateAim;
1360
- /**
1361
- * Called after speed skill calculation.
1362
- *
1363
- * @param speedSkill The speed skill.
1364
- */
1365
- private postCalculateSpeed;
1366
- /**
1367
- * Calculates speed-related attributes.
1368
- */
1369
- private calculateSpeedAttributes;
1385
+ speedStrain: number;
1370
1386
  /**
1371
- * Called after flashlight skill calculation.
1372
- *
1373
- * @param flashlightSkill The flashlight skill.
1387
+ * The flashlight strain generated by this hitobject.
1374
1388
  */
1375
- private postCalculateFlashlight;
1389
+ flashlightStrain: number;
1390
+ get smallCircleBonus(): number;
1391
+ protected readonly mode = Modes.osu;
1376
1392
  }
1377
1393
 
1378
1394
  /**
1379
- * A difficulty calculator that calculates for both osu!droid and osu!standard gamemode.
1395
+ * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
1396
+ * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
1380
1397
  */
1381
- declare class MapStars {
1382
- /**
1383
- * The osu!droid difficulty calculator of the beatmap.
1384
- */
1385
- readonly droid: DroidDifficultyCalculator;
1386
- /**
1387
- * The osu!standard difficulty calculator of the beatmap.
1388
- */
1389
- readonly osu: OsuDifficultyCalculator;
1390
- /**
1391
- * Constructs this instance and calculates the given beatmap's osu!droid and osu!standard difficulty.
1392
- *
1393
- * @param beatmap The beatmap to calculate.
1394
- * @param options Options for the difficulty calculation.
1395
- */
1396
- constructor(beatmap: Beatmap, options?: DifficultyCalculationOptions);
1398
+ declare abstract class OsuSkill extends StrainSkill {
1397
1399
  /**
1398
- * Returns a string representative of the class.
1400
+ * The weight by which each strain value decays.
1399
1401
  */
1400
- toString(): string;
1402
+ protected abstract readonly decayWeight: number;
1403
+ difficultyValue(): number;
1401
1404
  }
1402
1405
 
1403
1406
  /**
1404
1407
  * Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
1405
1408
  */
1406
1409
  declare class OsuAim extends OsuSkill {
1407
- protected readonly skillMultiplier: number;
1408
- protected readonly strainDecayBase: number;
1409
- protected readonly reducedSectionCount: number;
1410
- protected readonly reducedSectionBaseline: number;
1411
- protected readonly difficultyMultiplier: number;
1412
- protected readonly decayWeight: number;
1413
- private readonly withSliders;
1414
- constructor(mods: Mod[], withSliders: boolean);
1415
- /**
1416
- * @param current The hitobject to calculate.
1417
- */
1418
- protected strainValueAt(current: DifficultyHitObject): number;
1410
+ protected readonly strainDecayBase = 0.15;
1411
+ protected readonly reducedSectionCount = 10;
1412
+ protected readonly reducedSectionBaseline = 0.75;
1413
+ protected readonly decayWeight = 0.9;
1414
+ private currentAimStrain;
1415
+ private readonly skillMultiplier;
1416
+ private readonly sliderStrains;
1417
+ readonly withSliders: boolean;
1418
+ constructor(mods: ModMap, withSliders: boolean);
1419
+ /**
1420
+ * Obtains the amount of sliders that are considered difficult in terms of relative strain.
1421
+ */
1422
+ countDifficultSliders(): number;
1423
+ protected strainValueAt(current: OsuDifficultyHitObject): number;
1424
+ protected calculateInitialStrain(time: number, current: OsuDifficultyHitObject): number;
1419
1425
  /**
1420
1426
  * @param current The hitobject to save to.
1421
1427
  */
1422
- protected saveToHitObject(current: DifficultyHitObject): void;
1428
+ protected saveToHitObject(current: OsuDifficultyHitObject): void;
1423
1429
  }
1424
1430
 
1425
1431
  /**
1426
1432
  * An evaluator for calculating osu!standard Aim skill.
1427
1433
  */
1428
- declare abstract class OsuAimEvaluator extends AimEvaluator {
1434
+ declare abstract class OsuAimEvaluator {
1435
+ private static readonly wideAngleMultiplier;
1436
+ private static readonly acuteAngleMultiplier;
1437
+ private static readonly sliderMultiplier;
1438
+ private static readonly velocityChangeMultiplier;
1439
+ private static readonly wiggleMultiplier;
1429
1440
  /**
1430
1441
  * Evaluates the difficulty of aiming the current object, based on:
1431
1442
  *
@@ -1437,31 +1448,64 @@ declare abstract class OsuAimEvaluator extends AimEvaluator {
1437
1448
  * @param current The current object.
1438
1449
  * @param withSliders Whether to take slider difficulty into account.
1439
1450
  */
1440
- static evaluateDifficultyOf(current: DifficultyHitObject, withSliders: boolean): number;
1451
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject, withSliders: boolean): number;
1452
+ private static calculateWideAngleBonus;
1453
+ private static calculateAcuteAngleBonus;
1454
+ }
1455
+
1456
+ /**
1457
+ * Holds data that can be used to calculate osu!standard performance points.
1458
+ */
1459
+ declare class OsuDifficultyAttributes extends DifficultyAttributes implements IOsuDifficultyAttributes {
1460
+ approachRate: number;
1461
+ speedDifficulty: number;
1462
+ speedDifficultStrainCount: number;
1463
+ constructor(cacheableAttributes?: CacheableDifficultyAttributes<IOsuDifficultyAttributes>);
1464
+ toString(): string;
1465
+ }
1466
+
1467
+ /**
1468
+ * A difficulty calculator for osu!standard gamemode.
1469
+ */
1470
+ declare class OsuDifficultyCalculator extends DifficultyCalculator<OsuPlayableBeatmap, OsuDifficultyHitObject, OsuDifficultyAttributes> {
1471
+ protected readonly difficultyMultiplier = 0.0675;
1472
+ constructor();
1473
+ retainDifficultyAdjustmentMods(mods: Mod[]): Mod[];
1474
+ protected createDifficultyAttributes(beatmap: OsuPlayableBeatmap, skills: Skill[]): OsuDifficultyAttributes;
1475
+ protected createPlayableBeatmap(beatmap: Beatmap, mods?: ModMap): OsuPlayableBeatmap;
1476
+ protected createDifficultyHitObjects(beatmap: OsuPlayableBeatmap): OsuDifficultyHitObject[];
1477
+ protected createSkills(beatmap: OsuPlayableBeatmap): OsuSkill[];
1478
+ protected createStrainPeakSkills(beatmap: OsuPlayableBeatmap): StrainSkill[];
1479
+ private populateAimAttributes;
1480
+ private populateSpeedAttributes;
1481
+ private populateFlashlightAttributes;
1441
1482
  }
1442
1483
 
1443
1484
  /**
1444
1485
  * Represents the skill required to memorize and hit every object in a beatmap with the Flashlight mod enabled.
1445
1486
  */
1446
1487
  declare class OsuFlashlight extends OsuSkill {
1447
- protected readonly skillMultiplier: number;
1448
- protected readonly strainDecayBase: number;
1449
- protected readonly reducedSectionCount: number;
1450
- protected readonly reducedSectionBaseline: number;
1451
- protected readonly decayWeight: number;
1452
- private readonly isHidden;
1453
- constructor(mods: Mod[]);
1454
- /**
1455
- * @param current The hitobject to calculate.
1456
- */
1457
- protected strainValueAt(current: DifficultyHitObject): number;
1458
- protected saveToHitObject(current: DifficultyHitObject): void;
1488
+ protected readonly strainDecayBase = 0.15;
1489
+ protected readonly reducedSectionCount = 0;
1490
+ protected readonly reducedSectionBaseline = 1;
1491
+ protected readonly decayWeight = 1;
1492
+ private currentFlashlightStrain;
1493
+ private readonly skillMultiplier;
1494
+ difficultyValue(): number;
1495
+ protected strainValueAt(current: OsuDifficultyHitObject): number;
1496
+ protected calculateInitialStrain(time: number, current: OsuDifficultyHitObject): number;
1497
+ protected saveToHitObject(current: OsuDifficultyHitObject): void;
1459
1498
  }
1460
1499
 
1461
1500
  /**
1462
1501
  * An evaluator for calculating osu!standard Flashlight skill.
1463
1502
  */
1464
- declare abstract class OsuFlashlightEvaluator extends FlashlightEvaluator {
1503
+ declare abstract class OsuFlashlightEvaluator {
1504
+ private static readonly maxOpacityBonus;
1505
+ private static readonly hiddenBonus;
1506
+ private static readonly minVelocity;
1507
+ private static readonly sliderMultiplier;
1508
+ private static readonly minAngleMultiplier;
1465
1509
  /**
1466
1510
  * Evaluates the difficulty of memorizing and hitting the current object, based on:
1467
1511
  *
@@ -1472,15 +1516,15 @@ declare abstract class OsuFlashlightEvaluator extends FlashlightEvaluator {
1472
1516
  * - and whether Hidden mod is enabled.
1473
1517
  *
1474
1518
  * @param current The current object.
1475
- * @param isHiddenMod Whether the Hidden mod is enabled.
1519
+ * @param mods The mods used.
1476
1520
  */
1477
- static evaluateDifficultyOf(current: DifficultyHitObject, isHiddenMod: boolean): number;
1521
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject, mods: ModMap): number;
1478
1522
  }
1479
1523
 
1480
1524
  /**
1481
1525
  * A performance points calculator that calculates performance points for osu!standard gamemode.
1482
1526
  */
1483
- declare class OsuPerformanceCalculator extends PerformanceCalculator {
1527
+ declare class OsuPerformanceCalculator extends PerformanceCalculator<IOsuDifficultyAttributes> {
1484
1528
  /**
1485
1529
  * The aim performance value.
1486
1530
  */
@@ -1497,15 +1541,13 @@ declare class OsuPerformanceCalculator extends PerformanceCalculator {
1497
1541
  * The flashlight performance value.
1498
1542
  */
1499
1543
  flashlight: number;
1500
- readonly difficultyAttributes: OsuDifficultyAttributes;
1501
1544
  protected finalMultiplier: number;
1502
- protected readonly mode: Modes;
1503
- /**
1504
- * @param difficultyAttributes The difficulty attributes to calculate.
1505
- */
1506
- constructor(difficultyAttributes: OsuDifficultyAttributes);
1545
+ protected readonly mode = Modes.osu;
1546
+ private comboPenalty;
1547
+ private speedDeviation;
1507
1548
  protected calculateValues(): void;
1508
- protected calculateTotalValue(): void;
1549
+ protected calculateTotalValue(): number;
1550
+ protected handleOptions(options?: PerformanceCalculationOptions): void;
1509
1551
  /**
1510
1552
  * Calculates the aim performance value of the beatmap.
1511
1553
  */
@@ -1522,55 +1564,87 @@ declare class OsuPerformanceCalculator extends PerformanceCalculator {
1522
1564
  * Calculates the flashlight performance value of the beatmap.
1523
1565
  */
1524
1566
  private calculateFlashlightValue;
1567
+ /**
1568
+ * Estimates a player's deviation on speed notes using {@link calculateDeviation}, assuming worst-case.
1569
+ *
1570
+ * Treats all speed notes as hit circles.
1571
+ */
1572
+ private calculateSpeedDeviation;
1573
+ /**
1574
+ * Estimates the player's tap deviation based on the OD, given number of greats, oks, mehs and misses,
1575
+ * assuming the player's mean hit error is 0. The estimation is consistent in that two SS scores on the
1576
+ * same map with the same settings will always return the same deviation.
1577
+ *
1578
+ * Misses are ignored because they are usually due to misaiming.
1579
+ *
1580
+ * Greats and oks are assumed to follow a normal distribution, whereas mehs are assumed to follow a uniform distribution.
1581
+ */
1582
+ private calculateDeviation;
1583
+ /**
1584
+ * Calculates multiplier for speed to account for improper tapping based on the deviation and speed difficulty.
1585
+ *
1586
+ * [Graph](https://www.desmos.com/calculator/dmogdhzofn)
1587
+ */
1588
+ private calculateSpeedHighDeviationNerf;
1525
1589
  toString(): string;
1526
1590
  }
1527
1591
 
1528
1592
  /**
1529
1593
  * An evaluator for calculating osu!standard Rhythm skill.
1530
1594
  */
1531
- declare abstract class OsuRhythmEvaluator extends RhythmEvaluator {
1595
+ declare abstract class OsuRhythmEvaluator {
1596
+ private static readonly historyTimeMax;
1597
+ private static readonly historyObjectsMax;
1598
+ private static readonly rhythmOverallMultiplier;
1599
+ private static readonly rhythmRatioMultiplier;
1532
1600
  /**
1533
1601
  * Calculates a rhythm multiplier for the difficulty of the tap associated
1534
1602
  * with historic data of the current object.
1535
1603
  *
1536
1604
  * @param current The current object.
1537
- * @param greatWindow The great hit window of the current object.
1538
1605
  */
1539
- static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number): number;
1606
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject): number;
1540
1607
  }
1541
1608
 
1542
1609
  /**
1543
1610
  * Represents the skill required to press keys or tap with regards to keeping up with the speed at which objects need to be hit.
1544
1611
  */
1545
1612
  declare class OsuSpeed extends OsuSkill {
1546
- protected readonly skillMultiplier: number;
1547
- protected readonly strainDecayBase: number;
1548
- protected readonly reducedSectionCount: number;
1549
- protected readonly reducedSectionBaseline: number;
1550
- protected readonly difficultyMultiplier: number;
1551
- protected readonly decayWeight: number;
1613
+ protected readonly strainDecayBase = 0.3;
1614
+ protected readonly reducedSectionCount = 5;
1615
+ protected readonly reducedSectionBaseline = 0.75;
1616
+ protected readonly decayWeight = 0.9;
1552
1617
  private currentSpeedStrain;
1553
1618
  private currentRhythm;
1554
- private readonly greatWindow;
1555
- constructor(mods: Mod[], greatWindow: number);
1619
+ private readonly skillMultiplier;
1620
+ private maxStrain;
1621
+ /**
1622
+ * The amount of notes that are relevant to the difficulty.
1623
+ */
1624
+ relevantNoteCount(): number;
1556
1625
  /**
1557
1626
  * @param current The hitobject to calculate.
1558
1627
  */
1559
- protected strainValueAt(current: DifficultyHitObject): number;
1628
+ protected strainValueAt(current: OsuDifficultyHitObject): number;
1629
+ protected calculateInitialStrain(time: number, current: OsuDifficultyHitObject): number;
1560
1630
  /**
1561
1631
  * @param current The hitobject to save to.
1562
1632
  */
1563
- protected saveToHitObject(current: DifficultyHitObject): void;
1633
+ protected saveToHitObject(current: OsuDifficultyHitObject): void;
1564
1634
  }
1565
1635
 
1566
1636
  /**
1567
1637
  * An evaluator for calculating osu!standard speed skill.
1568
1638
  */
1569
- declare abstract class OsuSpeedEvaluator extends SpeedEvaluator {
1639
+ declare abstract class OsuSpeedEvaluator {
1570
1640
  /**
1571
1641
  * Spacing threshold for a single hitobject spacing.
1642
+ *
1643
+ * About 1.25 circles distance between hitobject centers.
1572
1644
  */
1573
1645
  private static readonly SINGLE_SPACING_THRESHOLD;
1646
+ private static readonly minSpeedBonus;
1647
+ private static readonly DISTANCE_MULTIPLIER;
1574
1648
  /**
1575
1649
  * Evaluates the difficulty of tapping the current object, based on:
1576
1650
  *
@@ -1579,9 +1653,9 @@ declare abstract class OsuSpeedEvaluator extends SpeedEvaluator {
1579
1653
  * - and how easily they can be cheesed.
1580
1654
  *
1581
1655
  * @param current The current object.
1582
- * @param greatWindow The great hit window of the current object.
1656
+ * @param mods The mods applied.
1583
1657
  */
1584
- static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number): number;
1658
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject, mods: ModMap): number;
1585
1659
  }
1586
1660
 
1587
- 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 };
1661
+ export { type CacheableDifficultyAttributes, type DifficultSlider, DifficultyAttributes, DifficultyCalculator, DifficultyHitObject, DroidAim, DroidAimEvaluator, DroidDifficultyAttributes, DroidDifficultyCalculator, DroidDifficultyHitObject, DroidFlashlight, DroidFlashlightEvaluator, DroidPerformanceCalculator, DroidReading, DroidReadingEvaluator, DroidRhythm, DroidRhythmEvaluator, DroidTap, DroidTapEvaluator, ExtendedDroidDifficultyAttributes, type HighStrainSection, type IDifficultyAttributes, type IDroidDifficultyAttributes, type IExtendedDroidDifficultyAttributes, type IOsuDifficultyAttributes, OsuAim, OsuAimEvaluator, OsuDifficultyAttributes, OsuDifficultyCalculator, OsuDifficultyHitObject, OsuFlashlight, OsuFlashlightEvaluator, OsuPerformanceCalculator, OsuRhythmEvaluator, OsuSpeed, OsuSpeedEvaluator, type PerformanceCalculationOptions, PerformanceCalculator, type StrainPeaks };