@rian8337/osu-difficulty-calculator 4.0.0-beta.1 → 4.0.0-beta.100

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,113 +1,9 @@
1
- import { MapStats, Mod, PlaceableHitObject, Modes, Beatmap, HitObject, Accuracy } from '@rian8337/osu-base';
1
+ import { PlaceableHitObject, Vector2, Modes, ModMap, HitResult, HitWindow, SerializedMod, PlayableBeatmap, Mod, Beatmap, Accuracy, DroidPlayableBeatmap, OsuPlayableBeatmap } from '@rian8337/osu-base';
2
2
 
3
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
- }
32
-
33
- /**
34
- * Holds data that can be used to calculate performance points.
35
- */
36
- interface DifficultyAttributes {
37
- /**
38
- * The mods which were applied to the beatmap.
39
- */
40
- mods: Mod[];
41
- /**
42
- * The combined star rating of all skills.
43
- */
44
- starRating: number;
45
- /**
46
- * The maximum achievable combo.
47
- */
48
- maxCombo: number;
49
- /**
50
- * The difficulty corresponding to the aim skill.
51
- */
52
- aimDifficulty: number;
53
- /**
54
- * The difficulty corresponding to the flashlight skill.
55
- */
56
- flashlightDifficulty: number;
57
- /**
58
- * The number of clickable objects weighted by difficulty.
59
- *
60
- * Related to speed/tap difficulty.
61
- */
62
- speedNoteCount: number;
63
- /**
64
- * Describes how much of aim difficulty is contributed to by hitcircles or sliders.
65
- *
66
- * A value closer to 1 indicates most of aim difficulty is contributed by hitcircles.
67
- *
68
- * A value closer to 0 indicates most of aim difficulty is contributed by sliders.
69
- */
70
- sliderFactor: number;
71
- /**
72
- * The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
73
- *
74
- * Rate-adjusting mods don't directly affect the approach rate difficulty value, but have a perceived effect as a result of adjusting audio timing.
75
- */
76
- approachRate: number;
77
- /**
78
- * The perceived overall difficulty inclusive of rate-adjusting mods (DT/HT/etc), based on osu!standard judgement.
79
- *
80
- * Rate-adjusting mods don't directly affect the overall difficulty value, but have a perceived effect as a result of adjusting audio timing.
81
- */
82
- overallDifficulty: number;
83
- /**
84
- * The number of hitcircles in the beatmap.
85
- */
86
- hitCircleCount: number;
87
- /**
88
- * The number of sliders in the beatmap.
89
- */
90
- sliderCount: number;
91
- /**
92
- * The number of spinners in the beatmap.
93
- */
94
- spinnerCount: number;
95
- }
96
-
97
- /**
98
- * Represents options for difficulty calculation.
99
- */
100
- interface DifficultyCalculationOptions extends CalculationOptions {
101
- /**
102
- * The modifications to apply.
103
- */
104
- mods?: Mod[];
105
- }
106
-
107
- /**
108
- * Represents an osu!standard hit object with difficulty calculation values.
4
+ * Represents a hit object with difficulty calculation values.
109
5
  */
110
- declare class DifficultyHitObject {
6
+ declare abstract class DifficultyHitObject {
111
7
  /**
112
8
  * The underlying hitobject.
113
9
  */
@@ -117,19 +13,7 @@ declare class DifficultyHitObject {
117
13
  *
118
14
  * This is one less than the actual index of the hitobject in the beatmap.
119
15
  */
120
- index: number;
121
- /**
122
- * The preempt time of the hitobject.
123
- */
124
- baseTimePreempt: number;
125
- /**
126
- * Adjusted preempt time of the hitobject, taking speed multiplier into account.
127
- */
128
- timePreempt: number;
129
- /**
130
- * The fade in time of the hitobject.
131
- */
132
- timeFadeIn: number;
16
+ readonly index: number;
133
17
  /**
134
18
  * The aim strain generated by the hitobject if sliders are considered.
135
19
  */
@@ -139,40 +23,21 @@ declare class DifficultyHitObject {
139
23
  */
140
24
  aimStrainWithoutSliders: number;
141
25
  /**
142
- * The tap strain generated by the hitobject.
143
- *
144
- * This is also used for osu!standard as opposed to "speed strain".
26
+ * The flashlight strain generated by the hitobject.
145
27
  */
146
- tapStrain: number;
28
+ flashlightStrain: number;
147
29
  /**
148
- * The tap strain generated by the hitobject if `strainTime` isn't modified by
149
- * OD. This is used in three-finger detection.
30
+ * The reading difficulty generated by the hitobject.
150
31
  */
151
- originalTapStrain: number;
32
+ readingDifficulty: number;
152
33
  /**
153
34
  * The rhythm multiplier generated by the hitobject. This is used to alter tap strain.
154
35
  */
155
36
  rhythmMultiplier: number;
156
37
  /**
157
- * The rhythm strain generated by the hitobject.
158
- */
159
- rhythmStrain: number;
160
- /**
161
- * The flashlight strain generated by the hitobject if sliders are considered.
162
- */
163
- flashlightStrainWithSliders: number;
164
- /**
165
- * The flashlight strain generated by the hitobject if sliders are not considered.
38
+ * The normalized distance from the start position of the previous hitobject to the start position of this hitobject.
166
39
  */
167
- flashlightStrainWithoutSliders: number;
168
- /**
169
- * The visual strain generated by the hitobject if sliders are considered.
170
- */
171
- visualStrainWithSliders: number;
172
- /**
173
- * The visual strain generated by the hitobject if sliders are not considered.
174
- */
175
- visualStrainWithoutSliders: number;
40
+ jumpDistance: number;
176
41
  /**
177
42
  * The normalized distance from the "lazy" end position of the previous hitobject to the start position of this hitobject.
178
43
  *
@@ -204,51 +69,114 @@ declare class DifficultyHitObject {
204
69
  * The time taken to travel through `travelDistance`, with a minimum value of 25ms for sliders.
205
70
  */
206
71
  travelTime: number;
72
+ /**
73
+ * The position of the cursor at the point of completion of this hitobject if it was hit
74
+ * with as few movements as possible.
75
+ */
76
+ lazyEndPosition?: Vector2;
77
+ /**
78
+ * The distance travelled by the cursor upon completion of this hitobject if it was hit
79
+ * with as few movements as possible.
80
+ */
81
+ lazyTravelDistance: number;
82
+ /**
83
+ * The time taken by the cursor upon completion of this hitobject if it was hit with
84
+ * as few movements as possible.
85
+ */
86
+ lazyTravelTime: number;
207
87
  /**
208
88
  * Angle the player has to take to hit this hitobject.
209
89
  *
210
90
  * Calculated as the angle between the circles (current-2, current-1, current).
211
91
  */
212
- angle: number | null;
92
+ angleSigned: number | null;
93
+ /**
94
+ * Unsigned angle the player has to take to hit this hitobject.
95
+ *
96
+ * Calculated as the angle between the circles (current-2, current-1, current).
97
+ */
98
+ get angle(): number | null;
99
+ /**
100
+ * Angle of the vector created between current and current-1 normalized to consider
101
+ * symmetrical vectors in any axis to be the same angle.
102
+ */
103
+ normalizedVectorAngle: number | null;
213
104
  /**
214
105
  * The amount of milliseconds elapsed between this hitobject and the last hitobject.
215
106
  */
216
- deltaTime: number;
107
+ readonly deltaTime: number;
217
108
  /**
218
109
  * The amount of milliseconds elapsed since the start time of the previous hitobject, with a minimum of 25ms.
219
110
  */
220
- strainTime: number;
111
+ readonly strainTime: number;
112
+ /**
113
+ * The amount of milliseconds elapsed between the last {@link DifficultyHitObject}'s {@link endTime} and
114
+ * this {@link DifficultyHitObject}'s {@link startTime} capped to a minimum of {@link minDeltaTime}ms.
115
+ */
116
+ readonly lastObjectEndDeltaTime: number;
221
117
  /**
222
118
  * Adjusted start time of the hitobject, taking speed multiplier into account.
223
119
  */
224
- startTime: number;
120
+ readonly startTime: number;
225
121
  /**
226
122
  * Adjusted end time of the hitobject, taking speed multiplier into account.
227
123
  */
228
- endTime: number;
124
+ readonly endTime: number;
229
125
  /**
230
- * The note density of the hitobject.
126
+ * Adjusted preempt time of the hitobject, taking speed multiplier into account.
231
127
  */
232
- noteDensity: number;
128
+ readonly timePreempt: number;
233
129
  /**
234
- * The overlapping factor of the hitobject.
235
- *
236
- * This is used to scale visual skill.
130
+ * The beatmap clock rate.
237
131
  */
238
- overlappingFactor: number;
132
+ readonly clockRate: number;
239
133
  /**
240
- * Adjusted velocity of the hitobject, taking speed multiplier into account.
134
+ * Selective bonus for beatmaps with higher circle size.
241
135
  */
242
- velocity: number;
136
+ abstract get smallCircleBonus(): number;
137
+ /**
138
+ * This {@link DifficultyHitObject}'s immediate overall difficulty value calculated from the raw hitwindow.
139
+ */
140
+ get overallDifficulty(): number;
243
141
  /**
244
142
  * Other hitobjects in the beatmap, including this hitobject.
245
143
  */
246
- private readonly hitObjects;
144
+ protected readonly hitObjects: readonly DifficultyHitObject[];
145
+ /**
146
+ * The normalized radius of the hitobject.
147
+ */
148
+ abstract readonly normalizedRadius: number;
149
+ /**
150
+ * The normalized diameter of the hitobject.
151
+ */
152
+ get normalizedDiameter(): number;
153
+ protected abstract readonly mode: Modes;
154
+ protected get maximumSliderRadius(): number;
155
+ protected get assumedSliderRadius(): number;
247
156
  /**
157
+ * The lowest possible delta time value.
158
+ */
159
+ static readonly minDeltaTime = 25;
160
+ private readonly lastObject;
161
+ private readonly lastDifficultyObject;
162
+ private readonly lastLastDifficultyObject;
163
+ /**
164
+ * Note: You **must** call `computeProperties` at some point due to how TypeScript handles
165
+ * overridden properties (see [this](https://github.com/microsoft/TypeScript/issues/1617) GitHub issue).
166
+ *
248
167
  * @param object The underlying hitobject.
249
- * @param hitObjects All difficulty hitobjects in the processed beatmap.
168
+ * @param lastObject The hitobject before this hitobject.
169
+ * @param difficultyHitObjects All difficulty hitobjects in the processed beatmap.
170
+ * @param clockRate The clock rate of the beatmap.
171
+ * @param index The index of this hitobject in the list of all hitobjects.
172
+ */
173
+ constructor(object: PlaceableHitObject, lastObject: PlaceableHitObject | null, difficultyHitObjects: readonly DifficultyHitObject[], clockRate: number, index: number);
174
+ /**
175
+ * Computes the properties of this hitobject.
176
+ *
177
+ * @param clockRate The clock rate of the beatmap.
250
178
  */
251
- constructor(object: PlaceableHitObject, hitObjects: DifficultyHitObject[]);
179
+ computeProperties(clockRate: number): void;
252
180
  /**
253
181
  * Gets the difficulty hitobject at a specific index with respect to the current
254
182
  * difficulty hitobject's index.
@@ -259,7 +187,7 @@ declare class DifficultyHitObject {
259
187
  * @returns The difficulty hitobject at the index with respect to the current
260
188
  * difficulty hitobject's index, `null` if the index is out of range.
261
189
  */
262
- previous(backwardsIndex: number): DifficultyHitObject | null;
190
+ previous(backwardsIndex: number): this | null;
263
191
  /**
264
192
  * Gets the difficulty hitobject at a specific index with respect to the current
265
193
  * difficulty hitobject's index.
@@ -270,121 +198,179 @@ declare class DifficultyHitObject {
270
198
  * @returns The difficulty hitobject at the index with respect to the current
271
199
  * difficulty hitobject's index, `null` if the index is out of range.
272
200
  */
273
- next(forwardsIndex: number): DifficultyHitObject | null;
201
+ next(forwardsIndex: number): this | null;
274
202
  /**
275
203
  * Calculates the opacity of the hitobject at a given time.
276
204
  *
277
205
  * @param time The time to calculate the hitobject's opacity at.
278
- * @param isHidden Whether Hidden mod is used.
279
- * @param mode The gamemode to calculate the opacity for.
206
+ * @param mods The mods used. Defaults to No Mod.
280
207
  * @returns The opacity of the hitobject at the given time.
281
208
  */
282
- opacityAt(time: number, isHidden: boolean, mode: Modes): number;
209
+ opacityAt(time: number, mods?: ModMap): number;
283
210
  /**
284
- * Determines whether this hitobject is considered overlapping with the hitobject before it.
285
- *
286
- * Keep in mind that "overlapping" in this case is overlapping to the point where both hitobjects
287
- * can be hit with just a single tap in osu!droid.
211
+ * How possible is it to doubletap this object together with the next one and get perfect
212
+ * judgement in range from 0 to 1.
288
213
  *
289
- * @param considerDistance Whether to consider the distance between both hitobjects.
290
- * @returns Whether the hitobject is considered overlapping.
214
+ * A value closer to 1 indicates a higher possibility.
291
215
  */
292
- isOverlapping(considerDistance: boolean): boolean;
216
+ getDoubletapness(nextObj: this | null): number;
217
+ /**
218
+ * Retrieves the full rate-adjusted hit window for a {@link HitResult}.
219
+ */
220
+ hitWindowFor(result: HitResult): number;
221
+ /**
222
+ * The {@link HitWindow} for this {@link DifficultyHitObject}.
223
+ */
224
+ protected get rawHitWindow(): HitWindow;
225
+ private setDistances;
226
+ private calculateAngle;
227
+ private calculateSliderAngle;
228
+ private calculateSliderCursorPosition;
229
+ private getEndCursorPosition;
293
230
  }
294
231
 
295
232
  /**
296
- * A bare minimal abstract skill for fully custom skill implementations.
297
- *
298
- * This class should be considered a "processing" class and not persisted.
233
+ * Holds data that can be used to calculate performance points.
299
234
  */
300
- declare abstract class Skill {
235
+ interface IDifficultyAttributes {
301
236
  /**
302
- * The mods that this skill processes.
237
+ * The mods which were applied to the beatmap.
303
238
  */
304
- protected readonly mods: Mod[];
305
- constructor(mods: Mod[]);
239
+ mods: ModMap;
306
240
  /**
307
- * Processes a hitobject.
308
- *
309
- * @param current The hitobject to process.
241
+ * The combined star rating of all skills.
310
242
  */
311
- abstract process(current: DifficultyHitObject): void;
243
+ starRating: number;
312
244
  /**
313
- * Returns the calculated difficulty value representing all hitobjects that have been processed up to this point.
245
+ * The maximum achievable combo.
314
246
  */
315
- abstract difficultyValue(): number;
316
- }
317
-
318
- /**
319
- * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
320
- * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
321
- */
322
- declare abstract class StrainSkill extends Skill {
247
+ maxCombo: number;
323
248
  /**
324
- * The strain of currently calculated hitobject.
249
+ * The difficulty corresponding to the aim skill.
325
250
  */
326
- protected currentStrain: number;
251
+ aimDifficulty: number;
327
252
  /**
328
- * The current section's strain peak.
253
+ * The difficulty corresponding to the flashlight skill.
329
254
  */
330
- protected currentSectionPeak: number;
255
+ flashlightDifficulty: number;
331
256
  /**
332
- * Strain peaks are stored here.
257
+ * The difficulty corresponding to the reading skill.
333
258
  */
334
- readonly strainPeaks: number[];
259
+ readingDifficulty: number;
335
260
  /**
336
- * The number of sections with the highest strains, which the peak strain reductions will apply to.
337
- * This is done in order to decrease their impact on the overall difficulty of the map for this skill.
261
+ * The number of clickable objects weighted by difficulty.
262
+ *
263
+ * Related to speed/tap difficulty.
338
264
  */
339
- protected abstract readonly reducedSectionCount: number;
265
+ speedNoteCount: number;
340
266
  /**
341
- * The baseline multiplier applied to the section with the biggest strain.
267
+ * Describes how much of aim difficulty is contributed to by hitcircles or sliders.
268
+ *
269
+ * A value closer to 1 indicates most of aim difficulty is contributed by hitcircles.
270
+ *
271
+ * A value closer to 0 indicates most of aim difficulty is contributed by sliders.
342
272
  */
343
- protected abstract readonly reducedSectionBaseline: number;
273
+ sliderFactor: number;
344
274
  /**
345
- * Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other.
275
+ * The overall clock rate that was applied to the beatmap.
346
276
  */
347
- protected abstract readonly skillMultiplier: number;
277
+ clockRate: number;
348
278
  /**
349
- * Determines how quickly strain decays for the given skill.
279
+ * The perceived overall difficulty **exclusive** of rate-adjusting mods (DT/HT/etc).
350
280
  *
351
- * For example, a value of 0.15 indicates that strain decays to 15% of its original value in one second.
281
+ * Rate-adjusting mods don't directly affect the overall difficulty value, but have a perceived effect as a result of adjusting audio timing.
352
282
  */
353
- protected abstract readonly strainDecayBase: number;
354
- private readonly sectionLength;
355
- private currentSectionEnd;
356
- private isFirstObject;
283
+ overallDifficulty: number;
357
284
  /**
358
- * Calculates the strain value of a hitobject and stores the value in it. This value is affected by previously processed objects.
359
- *
360
- * @param current The hitobject to process.
285
+ * The number of hitcircles in the beatmap.
361
286
  */
362
- process(current: DifficultyHitObject): void;
287
+ hitCircleCount: number;
363
288
  /**
364
- * Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty.
289
+ * The number of sliders in the beatmap.
290
+ */
291
+ sliderCount: number;
292
+ /**
293
+ * The number of spinners in the beatmap.
294
+ */
295
+ spinnerCount: number;
296
+ /**
297
+ * The number of sliders weighted by difficulty.
298
+ */
299
+ aimDifficultSliderCount: number;
300
+ /**
301
+ * The amount of strains that are considered difficult with respect to the aim skill.
365
302
  */
366
- saveCurrentPeak(): void;
303
+ aimDifficultStrainCount: number;
367
304
  /**
368
- * Calculates strain decay for a specified time frame.
305
+ * Describes how much of {@link aimDifficultStrainCount} is contributed to by circles or sliders.
369
306
  *
370
- * @param ms The time frame to calculate.
307
+ * A value closer to 0 indicates most of {@link aimDifficultStrainCount} is contributed by circles.
308
+ *
309
+ * A value closer to infinity indicates most of {@link aimDifficultStrainCount} is contributed by sliders.
371
310
  */
372
- protected strainDecay(ms: number): number;
311
+ aimTopWeightedSliderFactor: number;
373
312
  /**
374
- * Calculates the strain value at a hitobject.
313
+ * The amount of notes that are considered difficult with respect to the reading skill.
375
314
  */
376
- protected abstract strainValueAt(current: DifficultyHitObject): number;
315
+ readingDifficultNoteCount: number;
316
+ }
317
+
318
+ /**
319
+ * Represents difficulty attributes that can be cached.
320
+ */
321
+ type CacheableDifficultyAttributes<T extends IDifficultyAttributes> = Omit<T, "mods" | "toCacheableAttributes"> & {
377
322
  /**
378
- * Saves the current strain to a hitobject.
323
+ * The mods which were applied to the beatmap.
379
324
  */
380
- protected abstract saveToHitObject(current: DifficultyHitObject): void;
325
+ mods: SerializedMod[];
326
+ };
327
+
328
+ /**
329
+ * Holds data that can be used to calculate performance points.
330
+ */
331
+ declare abstract class DifficultyAttributes implements IDifficultyAttributes {
332
+ mods: ModMap;
333
+ starRating: number;
334
+ maxCombo: number;
335
+ aimDifficulty: number;
336
+ flashlightDifficulty: number;
337
+ readingDifficulty: number;
338
+ speedNoteCount: number;
339
+ sliderFactor: number;
340
+ clockRate: number;
341
+ overallDifficulty: number;
342
+ hitCircleCount: number;
343
+ sliderCount: number;
344
+ spinnerCount: number;
345
+ aimDifficultSliderCount: number;
346
+ aimDifficultStrainCount: number;
347
+ aimTopWeightedSliderFactor: number;
348
+ readingDifficultNoteCount: number;
349
+ constructor(cacheableAttributes?: CacheableDifficultyAttributes<IDifficultyAttributes>);
381
350
  /**
382
- * Sets the initial strain level for a new section.
351
+ * Converts this `DifficultyAttributes` instance to an attribute structure that can be cached.
383
352
  *
384
- * @param offset The beginning of the new section in milliseconds, adjusted by speed multiplier.
385
- * @param current The current hitobject.
353
+ * @returns The cacheable attributes.
386
354
  */
387
- private startNewSectionFrom;
355
+ toCacheableAttributes(): CacheableDifficultyAttributes<this>;
356
+ /**
357
+ * Returns a string representation of the difficulty attributes.
358
+ */
359
+ toString(): string;
360
+ }
361
+
362
+ /**
363
+ * Represents a strain peak at a specific point in time.
364
+ */
365
+ interface TimedStrainPeak {
366
+ /**
367
+ * The time at which this peak occurs, in milliseconds.
368
+ */
369
+ readonly time: number;
370
+ /**
371
+ * The strain value of this peak.
372
+ */
373
+ readonly value: number;
388
374
  }
389
375
 
390
376
  /**
@@ -394,264 +380,385 @@ interface StrainPeaks {
394
380
  /**
395
381
  * The strain peaks of aim difficulty if sliders are considered.
396
382
  */
397
- aimWithSliders: number[];
383
+ aimWithSliders: readonly TimedStrainPeak[];
398
384
  /**
399
385
  * The strain peaks of aim difficulty if sliders are not considered.
400
386
  */
401
- aimWithoutSliders: number[];
387
+ aimWithoutSliders: readonly TimedStrainPeak[];
402
388
  /**
403
389
  * The strain peaks of speed difficulty.
404
390
  */
405
- speed: number[];
391
+ speed: readonly TimedStrainPeak[];
406
392
  /**
407
393
  * The strain peaks of flashlight difficulty.
408
394
  */
409
- flashlight: number[];
395
+ flashlight: readonly TimedStrainPeak[];
410
396
  }
411
397
 
412
398
  /**
413
- * The base of difficulty calculators.
399
+ * An interface for `Skill`s that have peak difficulties.
414
400
  */
415
- declare abstract class DifficultyCalculator {
416
- /**
417
- * The calculated beatmap.
418
- */
419
- readonly beatmap: Beatmap;
420
- /**
421
- * The difficulty objects of the beatmap.
422
- */
423
- readonly objects: DifficultyHitObject[];
401
+ interface IHasPeakDifficulty {
424
402
  /**
425
- * The modifications applied.
403
+ * The peak difficulties calculated by this `Skill`, in chronological order.
426
404
  */
427
- mods: Mod[];
405
+ get peaks(): readonly TimedStrainPeak[];
406
+ }
407
+
408
+ /**
409
+ * A bare minimal abstract skill for fully custom skill implementations.
410
+ */
411
+ declare abstract class Skill {
428
412
  /**
429
- * The total star rating of the beatmap.
413
+ * The mods that this skill processes.
430
414
  */
431
- total: number;
415
+ protected readonly mods: ModMap;
416
+ private _objectDifficulties;
432
417
  /**
433
- * The map statistics of the beatmap after modifications are applied.
418
+ * The difficulties of {@link DifficultyHitObject}s, populated by {@link Skill.process}.
434
419
  */
435
- stats: MapStats;
420
+ protected get objectDifficulties(): readonly number[];
421
+ constructor(mods: ModMap);
436
422
  /**
437
- * The strain peaks of various calculated difficulties.
423
+ * Calculates the strain value of a hitobject and stores the value in it.
424
+ * This value is affected by previously processed objects.
425
+ *
426
+ * @param current The hitobject to process.
438
427
  */
439
- readonly strainPeaks: StrainPeaks;
428
+ process(current: DifficultyHitObject): void;
440
429
  /**
441
- * Holds data that can be used to calculate performance points.
430
+ * Returns the calculated difficulty value representing all hitobjects that have been processed up to this point.
442
431
  */
443
- abstract readonly attributes: DifficultyAttributes;
444
- protected readonly sectionLength: number;
445
- protected abstract readonly difficultyMultiplier: number;
446
- protected abstract readonly mode: Modes;
432
+ abstract difficultyValue(): number;
447
433
  /**
448
- * Constructs a new instance of the calculator.
434
+ * Saves the calculated difficulty to a {@link DifficultyHitObject}.
449
435
  *
450
- * @param beatmap The beatmap to calculate. This beatmap will be deep-cloned to prevent reference changes.
436
+ * @param current The {@link DifficultyHitObject} to save the difficulty to.
437
+ * @param difficulty The difficulty to save.
451
438
  */
452
- constructor(beatmap: Beatmap);
439
+ protected saveToHitObject(current: DifficultyHitObject, difficulty: number): void;
453
440
  /**
454
- * Calculates the star rating of the specified beatmap.
455
- *
456
- * The beatmap is analyzed in chunks of `sectionLength` duration.
457
- * For each chunk the highest hitobject strains are added to
458
- * a list which is then collapsed into a weighted sum, much
459
- * like scores are weighted on a user's profile.
460
- *
461
- * For subsequent chunks, the initial max strain is calculated
462
- * by decaying the previous hitobject's strain until the
463
- * beginning of the new chunk.
441
+ * Calculates the difficulty value of a hitobject and stores the value in it.
464
442
  *
465
- * @param options Options for the difficulty calculation.
466
- * @returns The current instance.
443
+ * @param current The hitobject to process.
444
+ */
445
+ protected abstract processInternal(current: DifficultyHitObject): number;
446
+ }
447
+
448
+ /**
449
+ * Wraps a `DifficultyAttributes` object and adds a time value for which the attribute is valid.
450
+ *
451
+ * Output by `DifficultyCalculator.calculateTimed` methods.
452
+ */
453
+ interface TimedDifficultyAttributes<TAttributes extends DifficultyAttributes> {
454
+ /**
455
+ * The non-clock-adjusted time value at which the attributes take effect.
456
+ */
457
+ readonly time: number;
458
+ /**
459
+ * The attributes.
460
+ */
461
+ readonly attributes: TAttributes;
462
+ /**
463
+ * The number of sliders in the beatmap up to this point.
464
+ */
465
+ readonly sliderCount: number;
466
+ /**
467
+ * The number of slider ticks in the beatmap up to this point.
468
+ */
469
+ readonly sliderTickCount: number;
470
+ /**
471
+ * The number of slider repeats in the beatmap up to this point.
467
472
  */
468
- calculate(options?: DifficultyCalculationOptions): this;
473
+ readonly sliderRepeatCount: number;
474
+ }
475
+
476
+ /**
477
+ * The base of a difficulty calculator.
478
+ */
479
+ declare abstract class DifficultyCalculator<TBeatmap extends PlayableBeatmap, THitObject extends DifficultyHitObject, TAttributes extends DifficultyAttributes> {
480
+ /**
481
+ * `Mod`s that adjust the difficulty of a beatmap.
482
+ */
483
+ protected readonly difficultyAdjustmentMods: (typeof Mod)[];
484
+ /**
485
+ * Retains `Mod`s that adjust a beatmap's difficulty from the specified mods.
486
+ *
487
+ * @param mods The mods to retain the difficulty adjustment mods from.
488
+ * @returns The retained difficulty adjustment mods.
489
+ */
490
+ abstract retainDifficultyAdjustmentMods(mods: Mod[]): Mod[];
469
491
  /**
470
- * Generates difficulty hitobjects for this calculator.
492
+ * Calculates the difficulty of a `Beatmap` with specific `Mod`s.
493
+ *
494
+ * @param beatmap The `Beatmap` whose difficulty is to be calculated.
495
+ * @param mods The `Mod`s to apply to the beatmap. Defaults to No Mod.
496
+ * @returns A `DifficultyAttributes` object describing the difficulty of the `Beatmap`.
471
497
  */
472
- generateDifficultyHitObjects(): void;
498
+ calculate(beatmap: Beatmap, mods?: ModMap): TAttributes;
473
499
  /**
474
- * Calculates the skills provided.
500
+ * Obtains the strain peaks of a `PlayableBeatmap`.
475
501
  *
476
- * @param skills The skills to calculate.
502
+ * @param beatmap The `PlayableBeatmap` whose strain peaks are to be calculated.
503
+ * @returns The strain peaks of the `PlayableBeatmap`.
477
504
  */
478
- protected calculateSkills(...skills: StrainSkill[]): void;
505
+ calculateStrainPeaks(beatmap: TBeatmap): StrainPeaks;
479
506
  /**
480
- * Calculates the total star rating of the beatmap and stores it in this instance.
507
+ * Obtains the strain peaks of a `Beatmap` with specific `Mod`s.
508
+ *
509
+ * @param beatmap The `Beatmap` whose strain peaks are to be calculated.
510
+ * @param mods The `Mod`s to apply to the beatmap. Defaults to No Mod.
511
+ * @returns The strain peaks of the `Beatmap`.
481
512
  */
482
- abstract calculateTotal(): void;
513
+ calculateStrainPeaks(beatmap: Beatmap, mods?: ModMap): StrainPeaks;
483
514
  /**
484
- * Calculates every star rating of the beatmap and stores it in this instance.
515
+ * Calculates the difficulty of a `Beatmap` with specific `Mod`s and returns a set of
516
+ * `TimedDifficultyAttributes` representing the difficulty at every relevant time value in the `Beatmap`.
517
+ *
518
+ * @param beatmap The `Beatmap` whose difficulty is to be calculated.
519
+ * @param mods The `Mod`s to apply to the `Beatmap`.
520
+ * @return The set of `TimedDifficultyAttributes`.
485
521
  */
486
- abstract calculateAll(): void;
522
+ calculateTimed(beatmap: Beatmap, mods?: ModMap): TimedDifficultyAttributes<TAttributes>[];
487
523
  /**
488
- * Returns a string representative of the class.
524
+ * Creates the `Skill`s to calculate the difficulty of a `PlayableBeatmap`.
525
+ *
526
+ * @param beatmap The `PlayableBeatmap` whose difficulty will be calculated.
527
+ * @returns The `Skill`s.
489
528
  */
490
- abstract toString(): string;
529
+ protected abstract createSkills(beatmap: TBeatmap): Skill[];
491
530
  /**
492
- * Creates skills to be calculated.
531
+ * Creates the `Skill`s to obtain the strain peaks of a `PlayableBeatmap`.
532
+ *
533
+ * @param beatmap The `PlayableBeatmap` whose strain peaks will be calculated.
534
+ * @returns The `Skill`s.
493
535
  */
494
- protected abstract createSkills(): StrainSkill[];
536
+ protected abstract createStrainPeakSkills(beatmap: TBeatmap): (Skill & IHasPeakDifficulty)[];
495
537
  /**
496
- * Populates the stored difficulty attributes with necessary data.
538
+ * Creates difficulty hitobjects for this calculator.
539
+ *
540
+ * @param beatmap The beatmap to generate difficulty hitobjects from.
541
+ * @returns The generated difficulty hitobjects.
497
542
  */
498
- protected populateDifficultyAttributes(): void;
543
+ protected abstract createDifficultyHitObjects(beatmap: TBeatmap): THitObject[];
499
544
  /**
500
- * Calculates the star rating value of a difficulty.
545
+ * Creates a `DifficultyAttributes` object to describe a `PlayableBeatmap`'s difficulty.
501
546
  *
502
- * @param difficulty The difficulty to calculate.
547
+ * @param beatmap The `Beatmap` whose difficulty was calculated.
548
+ * @param playableBeatmap The `PlayableBeatmap` whose difficulty was calculated.
549
+ * @param skills The `Skill`s which processed the `PlayableBeatmap`.
550
+ * @param objects The `DifficultyHitObject`s which were processed.
551
+ * @returns The `DifficultyAttributes` object.
503
552
  */
504
- protected starValue(difficulty: number): number;
553
+ protected abstract createDifficultyAttributes(beatmap: Beatmap, playableBeatmap: PlayableBeatmap, skills: Skill[], objects: THitObject[]): TAttributes;
505
554
  /**
506
- * Calculates the base performance value of a difficulty rating.
555
+ * Constructs a `PlayableBeatmap` from a `Beatmap` with specific `Mod`s.
507
556
  *
508
- * @param rating The difficulty rating.
557
+ * @param beatmap The `Beatmap` to create a `PlayableBeatmap` from.
558
+ * @param mods The `Mod`s to apply to the `Beatmap`.
559
+ * @returns The `PlayableBeatmap`.
509
560
  */
510
- protected basePerformanceValue(rating: number): number;
561
+ protected abstract createPlayableBeatmap(beatmap: Beatmap, mods?: ModMap): TBeatmap;
511
562
  }
512
563
 
513
564
  /**
514
- * A converter used to convert normal hitobjects into difficulty hitobjects.
565
+ * Represents options for performance calculation.
515
566
  */
516
- declare class DifficultyHitObjectCreator {
567
+ interface PerformanceCalculationOptions {
517
568
  /**
518
- * The threshold for small circle buff for osu!droid.
569
+ * The maximum combo achieved in the score.
519
570
  */
520
- private readonly DROID_CIRCLESIZE_BUFF_THRESHOLD;
571
+ combo?: number;
521
572
  /**
522
- * The threshold for small circle buff for osu!standard.
573
+ * The accuracy achieved in the score.
523
574
  */
524
- private readonly PC_CIRCLESIZE_BUFF_THRESHOLD;
575
+ accPercent?: Accuracy | number;
525
576
  /**
526
- * The gamemode this creator is creating for.
577
+ * The amount of misses achieved in the score.
578
+ *
579
+ * If {@link accPercent} is provided as an {@link Accuracy} object, this value will be ignored.
527
580
  */
528
- private mode;
581
+ miss?: number;
529
582
  /**
530
- * The base normalized radius of hitobjects.
583
+ * The amount of slider ends dropped in the score.
531
584
  */
532
- private readonly normalizedRadius;
533
- private maximumSliderRadius;
534
- private readonly assumedSliderRadius;
535
- private readonly minDeltaTime;
585
+ sliderEndsDropped?: number;
536
586
  /**
537
- * Generates difficulty hitobjects for difficulty calculation.
587
+ * The amount of slider ticks missed in the score.
538
588
  */
539
- generateDifficultyObjects(params: {
540
- objects: readonly HitObject[];
541
- circleSize: number;
542
- mods: Mod[];
543
- speedMultiplier: number;
544
- mode: Modes;
545
- preempt?: number;
546
- }): DifficultyHitObject[];
589
+ sliderTicksMissed?: number;
547
590
  /**
548
- * Calculates a slider's cursor position.
591
+ * The tap penalty to apply for penalized scores. Only used when using `DroidPerformanceCalculator`.
549
592
  */
550
- private calculateSliderCursorPosition;
593
+ tapPenalty?: number;
551
594
  /**
552
- * Gets the scaling factor of a radius.
553
- *
554
- * @param radius The radius to get the scaling factor from.
595
+ * The aim slider cheese penalty to apply for penalized scores. Only used when using `DroidPerformanceCalculator`.
555
596
  */
556
- private getScalingFactor;
597
+ sliderCheesePenalty?: number;
557
598
  /**
558
- * Returns the end cursor position of a hitobject.
599
+ * The total score achieved in the score.
559
600
  */
560
- private getEndCursorPosition;
561
- private applyToOverlappingFactor;
601
+ totalScore?: number;
562
602
  }
563
603
 
564
604
  /**
565
- * Represents a slider that is considered difficult.
566
- *
567
- * This structure is a part of difficulty attributes and can be cached.
605
+ * Represents the calculated performance of a score.
568
606
  */
569
- interface DifficultSlider {
607
+ interface IPerformanceAttributes {
570
608
  /**
571
- * The index of the slider in the beatmap.
609
+ * Calculated score performance points.
572
610
  */
573
- readonly index: number;
611
+ total: number;
574
612
  /**
575
- * The difficulty rating of this slider compared to other sliders, based on the velocity of the slider.
576
- *
577
- * A value closer to 1 indicates that this slider is more difficult compared to most sliders.
578
- *
579
- * A value closer to 0 indicates that this slider is easier compared to most sliders.
613
+ * The amount of misses, including slider breaks.
580
614
  */
581
- readonly difficultyRating: number;
615
+ effectiveMissCount: number;
582
616
  }
583
617
 
584
618
  /**
585
- * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
586
- * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
619
+ * The base class of performance calculators.
587
620
  */
588
- declare abstract class DroidSkill extends StrainSkill {
621
+ declare abstract class PerformanceCalculator<TDifficultyAttributes extends IDifficultyAttributes, TPerformanceAttributes extends IPerformanceAttributes> {
589
622
  /**
590
- * The bonus multiplier that is given for a sequence of notes of equal difficulty.
623
+ * The calculated accuracy.
591
624
  */
592
- protected abstract readonly starsPerDouble: number;
593
- difficultyValue(): number;
594
- }
595
-
596
- /**
597
- * Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
598
- */
599
- declare class DroidAim extends DroidSkill {
600
- protected readonly skillMultiplier: number;
601
- protected readonly strainDecayBase: number;
602
- protected readonly reducedSectionCount: number;
603
- protected readonly reducedSectionBaseline: number;
604
- protected readonly starsPerDouble: number;
605
- private readonly withSliders;
606
- constructor(mods: Mod[], withSliders: boolean);
625
+ computedAccuracy: Accuracy;
607
626
  /**
608
- * @param current The hitobject to calculate.
627
+ * The calculated maximum combo.
628
+ */
629
+ combo: number;
630
+ /**
631
+ * The difficulty attributes that is being calculated.
632
+ */
633
+ readonly difficultyAttributes: TDifficultyAttributes | CacheableDifficultyAttributes<TDifficultyAttributes>;
634
+ /**
635
+ * The mods that were used.
636
+ */
637
+ protected readonly mods: ModMap;
638
+ private _sliderEndsDropped;
639
+ /**
640
+ * The amount of slider ends dropped in the score.
641
+ */
642
+ protected get sliderEndsDropped(): number;
643
+ private _sliderTicksMissed;
644
+ /**
645
+ * The amount of slider ticks missed in the score.
646
+ *
647
+ * This is used to calculate the slider accuracy.
648
+ */
649
+ protected get sliderTicksMissed(): number;
650
+ private _usingClassicSliderAccuracy;
651
+ /**
652
+ * Whether this score uses classic slider accuracy.
653
+ */
654
+ protected get usingClassicSliderAccuracy(): boolean;
655
+ /**
656
+ * @param difficultyAttributes The difficulty attributes to calculate.
657
+ */
658
+ constructor(difficultyAttributes: TDifficultyAttributes | CacheableDifficultyAttributes<TDifficultyAttributes>);
659
+ /**
660
+ * Calculates the performance points of the beatmap.
661
+ *
662
+ * @param options Options for performance calculation.
663
+ * @returns The attributes representing the performance.
609
664
  */
610
- protected strainValueAt(current: DifficultyHitObject): number;
665
+ calculate(options?: PerformanceCalculationOptions): TPerformanceAttributes;
611
666
  /**
612
- * @param current The hitobject to save to.
667
+ * Creates the performance attributes for this calculator.
668
+ *
669
+ * @returns The performance attributes.
670
+ */
671
+ protected abstract createPerformanceAttributes(): TPerformanceAttributes;
672
+ /**
673
+ * The total hits that can be done in the beatmap.
674
+ */
675
+ protected get totalHits(): number;
676
+ /**
677
+ * The total hits that were successfully done.
678
+ */
679
+ protected get totalSuccessfulHits(): number;
680
+ /**
681
+ * The total of imperfect hits (100s, 50s, misses).
682
+ */
683
+ protected get totalImperfectHits(): number;
684
+ /**
685
+ * Processes given options for usage in performance calculation.
686
+ *
687
+ * @param options Options for performance calculation.
613
688
  */
614
- protected saveToHitObject(current: DifficultyHitObject): void;
689
+ protected handleOptions(options?: PerformanceCalculationOptions): void;
690
+ /**
691
+ * Determines whether an attribute is a cacheable attribute.
692
+ *
693
+ * @param attributes The attributes to check.
694
+ * @returns Whether the attributes are cacheable.
695
+ */
696
+ private isCacheableAttribute;
615
697
  }
616
698
 
617
699
  /**
618
- * An evaluator for calculating osu!droid Aim skill.
700
+ * Represents an osu!droid hit object with difficulty calculation values.
619
701
  */
620
- declare abstract class DroidAimEvaluator extends AimEvaluator {
621
- protected static readonly wideAngleMultiplier: number;
622
- protected static readonly sliderMultiplier: number;
623
- protected static readonly velocityChangeMultiplier: number;
702
+ declare class DroidDifficultyHitObject extends DifficultyHitObject {
624
703
  /**
625
- * Spacing threshold for a single hitobject spacing.
704
+ * The tap difficulty generated by the hitobject.
626
705
  */
627
- private static readonly SINGLE_SPACING_THRESHOLD;
628
- private static readonly minSpeedBonus;
706
+ tapDifficulty: number;
629
707
  /**
630
- * Evaluates the difficulty of aiming the current object, based on:
708
+ * The tap difficulty generated by the hitobject if `strainTime` isn't modified by
709
+ * OD. This is used in three-finger detection.
710
+ */
711
+ originalTapDifficulty: number;
712
+ /**
713
+ * The rhythm difficulty generated by the hitobject.
714
+ */
715
+ rhythmDifficulty: number;
716
+ readonly normalizedRadius = 50;
717
+ protected readonly mode = Modes.Droid;
718
+ protected get maximumSliderRadius(): number;
719
+ get smallCircleBonus(): number;
720
+ opacityAt(time: number, mods?: ModMap): number;
721
+ previous(backwardsIndex: number): this | null;
722
+ next(forwardsIndex: number): this | null;
723
+ /**
724
+ * Determines whether this hitobject is considered overlapping with the hitobject before it.
631
725
  *
632
- * - cursor velocity to the current object,
633
- * - angle difficulty,
634
- * - sharp velocity increases,
635
- * - and slider difficulty.
726
+ * Keep in mind that "overlapping" in this case is overlapping to the point where both hitobjects
727
+ * can be hit with just a single tap in osu!droid.
636
728
  *
637
- * @param current The current object.
638
- * @param withSliders Whether to take slider difficulty into account.
729
+ * In the case of sliders, it is considered overlapping if all nested hitobjects can be hit with
730
+ * one aim motion.
731
+ *
732
+ * @param considerDistance Whether to consider the distance between both hitobjects.
733
+ * @returns Whether the hitobject is considered overlapping.
639
734
  */
640
- static evaluateDifficultyOf(current: DifficultyHitObject, withSliders: boolean): number;
735
+ isOverlapping(considerDistance: boolean): boolean;
736
+ }
737
+
738
+ /**
739
+ * Represents a slider that is considered difficult.
740
+ *
741
+ * This structure is a part of difficulty attributes and can be cached.
742
+ */
743
+ interface DifficultSlider {
641
744
  /**
642
- * Calculates the aim strain of a hitobject.
745
+ * The index of the slider in the beatmap.
643
746
  */
644
- private static aimStrainOf;
747
+ readonly index: number;
645
748
  /**
646
- * Calculates the movement strain of a hitobject.
749
+ * The difficulty rating of this slider compared to other sliders, based on the velocity of the slider.
750
+ *
751
+ * A value closer to 1 indicates that this slider is more difficult compared to most sliders.
752
+ *
753
+ * A value closer to 0 indicates that this slider is easier compared to most sliders.
647
754
  */
648
- private static movementStrainOf;
755
+ readonly difficultyRating: number;
649
756
  }
650
757
 
651
758
  /**
652
759
  * Holds data that can be used to calculate osu!droid performance points.
653
760
  */
654
- interface DroidDifficultyAttributes extends DifficultyAttributes {
761
+ interface IDroidDifficultyAttributes extends IDifficultyAttributes {
655
762
  /**
656
763
  * The difficulty corresponding to the tap skill.
657
764
  */
@@ -661,9 +768,34 @@ interface DroidDifficultyAttributes extends DifficultyAttributes {
661
768
  */
662
769
  rhythmDifficulty: number;
663
770
  /**
664
- * The difficulty corresponding to the visual skill.
771
+ * The amount of strains that are considered difficult with respect to the tap skill.
772
+ */
773
+ tapDifficultStrainCount: number;
774
+ /**
775
+ * Describes how much of {@link tapDifficultStrainCount} is contributed to by circles or sliders.
776
+ *
777
+ * A value closer to 0 indicates most of {@link tapDifficultStrainCount} is contributed by circles.
778
+ *
779
+ * A value closer to infinity indicates most of {@link tapDifficultStrainCount} is contributed by sliders.
780
+ */
781
+ tapTopWeightedSliderFactor: number;
782
+ /**
783
+ * The maximum score obtainable on the beatmap.
665
784
  */
666
- visualDifficulty: number;
785
+ maximumScore: number;
786
+ }
787
+
788
+ /**
789
+ * Holds data that can be used to calculate osu!droid performance points.
790
+ */
791
+ declare class DroidDifficultyAttributes extends DifficultyAttributes implements IDroidDifficultyAttributes {
792
+ tapDifficulty: number;
793
+ tapTopWeightedSliderFactor: number;
794
+ rhythmDifficulty: number;
795
+ tapDifficultStrainCount: number;
796
+ maximumScore: number;
797
+ constructor(cacheableAttributes?: CacheableDifficultyAttributes<IDroidDifficultyAttributes>);
798
+ toString(): string;
667
799
  }
668
800
 
669
801
  /**
@@ -688,7 +820,11 @@ interface HighStrainSection {
688
820
  * Holds data that can be used to calculate osu!droid performance points as well
689
821
  * as doing some analysis using the replay of a score.
690
822
  */
691
- interface ExtendedDroidDifficultyAttributes extends DroidDifficultyAttributes {
823
+ interface IExtendedDroidDifficultyAttributes extends IDroidDifficultyAttributes {
824
+ /**
825
+ * The mode of the difficulty calculation.
826
+ */
827
+ mode: "live";
692
828
  /**
693
829
  * Possible sections at which the player can use three fingers on.
694
830
  */
@@ -697,180 +833,212 @@ interface ExtendedDroidDifficultyAttributes extends DroidDifficultyAttributes {
697
833
  * Sliders that are considered difficult.
698
834
  */
699
835
  difficultSliders: DifficultSlider[];
700
- /**
701
- * The number of clickable objects weighted by difficulty.
702
- *
703
- * Related to aim difficulty.
704
- */
705
- aimNoteCount: number;
706
- /**
707
- * Describes how much of flashlight difficulty is contributed to by hitcircles or sliders.
708
- *
709
- * A value closer to 1 indicates most of flashlight difficulty is contributed by hitcircles.
710
- *
711
- * A value closer to 0 indicates most of flashlight difficulty is contributed by sliders.
712
- */
713
- flashlightSliderFactor: number;
714
- /**
715
- * Describes how much of visual difficulty is contributed to by hitcircles or sliders.
716
- *
717
- * A value closer to 1 indicates most of visual difficulty is contributed by hitcircles.
718
- *
719
- * A value closer to 0 indicates most of visual difficulty is contributed by sliders.
720
- */
721
- visualSliderFactor: number;
836
+ }
837
+
838
+ /**
839
+ * Holds data that can be used to calculate osu!droid performance points as well
840
+ * as doing some analysis using the replay of a score.
841
+ */
842
+ declare class ExtendedDroidDifficultyAttributes extends DroidDifficultyAttributes implements IExtendedDroidDifficultyAttributes {
843
+ mode: "live";
844
+ possibleThreeFingeredSections: HighStrainSection[];
845
+ difficultSliders: DifficultSlider[];
846
+ constructor(cacheableAttributes?: CacheableDifficultyAttributes<IExtendedDroidDifficultyAttributes>);
722
847
  }
723
848
 
724
849
  /**
725
850
  * A difficulty calculator for osu!droid gamemode.
726
851
  */
727
- declare class DroidDifficultyCalculator extends DifficultyCalculator {
852
+ declare class DroidDifficultyCalculator extends DifficultyCalculator<DroidPlayableBeatmap, DroidDifficultyHitObject, ExtendedDroidDifficultyAttributes> {
853
+ constructor();
854
+ retainDifficultyAdjustmentMods(mods: Mod[]): Mod[];
855
+ protected createDifficultyAttributes(beatmap: Beatmap, playableBeatmap: PlayableBeatmap, skills: Skill[], objects: DroidDifficultyHitObject[]): ExtendedDroidDifficultyAttributes;
856
+ protected createPlayableBeatmap(beatmap: Beatmap, mods?: ModMap): DroidPlayableBeatmap;
857
+ protected createDifficultyHitObjects(beatmap: DroidPlayableBeatmap): DroidDifficultyHitObject[];
858
+ protected createSkills(beatmap: DroidPlayableBeatmap): Skill[];
859
+ protected createStrainPeakSkills(beatmap: DroidPlayableBeatmap): (Skill & IHasPeakDifficulty)[];
860
+ private populateAimAttributes;
861
+ private populateTapAttributes;
862
+ private populateRhythmAttributes;
863
+ private populateFlashlightAttributes;
864
+ private populateReadingAttributes;
865
+ private calculateAimDifficultyRating;
866
+ private calculateDifficultyRating;
867
+ static sumCognitionDifficulty(reading: number, flashlight: number): number;
868
+ }
869
+
870
+ /**
871
+ * Represents the calculated performance of a score in osu!droid.
872
+ */
873
+ interface IDroidPerformanceAttributes extends IPerformanceAttributes {
728
874
  /**
729
- * The aim star rating of the beatmap.
875
+ * The aim performance points.
730
876
  */
731
877
  aim: number;
732
878
  /**
733
- * The tap star rating of the beatmap.
879
+ * The tap performance points.
734
880
  */
735
881
  tap: number;
736
882
  /**
737
- * The rhythm star rating of the beatmap.
883
+ * The accuracy performance points.
738
884
  */
739
- rhythm: number;
885
+ accuracy: number;
740
886
  /**
741
- * The flashlight star rating of the beatmap.
887
+ * The flashlight performance points.
742
888
  */
743
889
  flashlight: number;
744
890
  /**
745
- * The visual star rating of the beatmap.
891
+ * The reading performance points.
746
892
  */
747
- visual: number;
893
+ reading: number;
748
894
  /**
749
- * The strain threshold to start detecting for possible three-fingered section.
750
- *
751
- * Increasing this number will result in less sections being flagged.
895
+ * The penalty used to penalize the tap performance points.
752
896
  */
753
- static readonly threeFingerStrainThreshold: number;
754
- readonly attributes: ExtendedDroidDifficultyAttributes;
755
- protected readonly difficultyMultiplier: number;
756
- protected readonly mode: Modes;
897
+ tapPenalty: number;
757
898
  /**
758
- * Calculates the aim star rating of the beatmap and stores it in this instance.
899
+ * The estimated deviation of the score.
759
900
  */
760
- calculateAim(): void;
901
+ deviation: number;
761
902
  /**
762
- * Calculates the tap star rating of the beatmap and stores it in this instance.
903
+ * The estimated tap deviation of the score.
763
904
  */
764
- calculateTap(): void;
905
+ tapDeviation: number;
765
906
  /**
766
- * Calculates the rhythm star rating of the beatmap and stores it in this instance.
907
+ * The penalty used to penalize the aim performance points.
767
908
  */
768
- calculateRhythm(): void;
909
+ sliderCheesePenalty: number;
910
+ }
911
+
912
+ /**
913
+ * A performance points calculator that calculates performance points for osu!droid gamemode.
914
+ */
915
+ declare class DroidPerformanceCalculator extends PerformanceCalculator<IDroidDifficultyAttributes, IDroidPerformanceAttributes> {
769
916
  /**
770
- * Calculates the flashlight star rating of the beatmap and stores it in this instance.
917
+ * The total score achieved in the score.
771
918
  */
772
- calculateFlashlight(): void;
919
+ get totalScore(): number | null;
920
+ static readonly finalMultiplier = 1.24;
921
+ static readonly normExponent = 1.1;
922
+ private _sliderCheesePenalty;
923
+ private _tapPenalty;
924
+ private _effectiveMissCount;
925
+ private _deviation;
926
+ private _tapDeviation;
927
+ private _totalScore;
928
+ protected createPerformanceAttributes(): IDroidPerformanceAttributes;
929
+ protected handleOptions(options?: PerformanceCalculationOptions): void;
773
930
  /**
774
- * Calculates the visual star rating of the beatmap and stores it in this instance.
931
+ * Calculates the aim performance value of the beatmap.
775
932
  */
776
- calculateVisual(): void;
777
- calculateTotal(): void;
778
- calculateAll(): void;
933
+ private calculateAimValue;
779
934
  /**
780
- * Returns a string representative of the class.
935
+ * Calculates the tap performance value of the beatmap.
781
936
  */
782
- toString(): string;
937
+ private calculateTapValue;
783
938
  /**
784
- * Creates skills to be calculated.
939
+ * Calculates the accuracy performance value of the beatmap.
785
940
  */
786
- protected createSkills(): DroidSkill[];
941
+ private calculateAccuracyValue;
787
942
  /**
788
- * Called after aim skill calculation.
789
- *
790
- * @param aimSkill The aim skill that considers sliders.
791
- * @param aimSkillWithoutSliders The aim skill that doesn't consider sliders.
943
+ * Calculates the flashlight performance value of the beatmap.
792
944
  */
793
- private postCalculateAim;
945
+ private calculateFlashlightValue;
794
946
  /**
795
- * Calculates aim-related attributes.
947
+ * Calculates the reading performance value of the beatmap.
796
948
  */
797
- private calculateAimAttributes;
949
+ private calculateReadingValue;
798
950
  /**
799
- * Called after tap skill calculation.
951
+ * Calculates a strain-based miss penalty.
800
952
  *
801
- * @param tapSkill The tap skill.
953
+ * Strain-based miss penalty assumes that a player will miss on the hardest parts of a map,
954
+ * so we use the amount of relatively difficult sections to adjust miss penalty
955
+ * to make it more punishing on maps with lower amount of hard sections.
956
+ */
957
+ private calculateStrainBasedMissPenalty;
958
+ /**
959
+ * The object-based proportional miss penalty.
802
960
  */
803
- private postCalculateTap;
961
+ private get proportionalMissPenalty();
804
962
  /**
805
- * Calculates speed-related attributes.
963
+ * Calculates the object-based length scaling based on the deviation of a player for a full
964
+ * combo in this beatmap, taking retries into account.
965
+ *
966
+ * @param objectCount The amount of objects to be considered. Defaults to the amount of
967
+ * objects in this beatmap.
968
+ * @param punishForMemorization Whether to punish the deviation for memorization. Defaults to `false`.
806
969
  */
807
- private calculateSpeedAttributes;
970
+ private calculateDeviationBasedLengthScaling;
808
971
  /**
809
- * Calculates the sum of strains for possible three-fingered sections.
972
+ * Estimates the player's deviation based on the OD, number of circles and sliders,
973
+ * and number of 300s, 100s, 50s, and misses, assuming the player's mean hit error is 0.
974
+ *
975
+ * The estimation is consistent in that two SS scores on the same map
976
+ * with the same settings will always return the same deviation.
810
977
  *
811
- * @param firstObjectIndex The index of the first object in the section.
812
- * @param lastObjectIndex The index of the last object in the section.
813
- * @returns The summed strain of the section.
978
+ * Sliders are treated as circles with a 50 hit window.
979
+ *
980
+ * Misses are ignored because they are usually due to misaiming, and 50s
981
+ * are grouped with 100s since they are usually due to misreading.
982
+ *
983
+ * Inaccuracies are capped to the number of circles in the map.
814
984
  */
815
- private calculateThreeFingerSummedStrain;
985
+ private calculateAimDeviation;
816
986
  /**
817
- * Called after rhythm skill calculation.
987
+ * Does the same as {@link calculateAimDeviation}, but only for notes and inaccuracies that are relevant to tap difficulty.
818
988
  *
819
- * @param rhythmSkill The rhythm skill.
989
+ * Treats all difficult speed notes as circles, so this method can sometimes return a lower deviation than {@link calculateAimDeviation}.
990
+ * This is fine though, since this method is only used to scale tap pp.
820
991
  */
821
- private postCalculateRhythm;
992
+ private calculateTapDeviation;
822
993
  /**
823
- * Called after flashlight skill calculation.
994
+ * Estimates the player's tap deviation based on the OD, given number of greats, oks, mehs and misses,
995
+ * assuming the player's mean hit error is 0. The estimation is consistent in that two SS scores on the
996
+ * same map with the same settings will always return the same deviation.
824
997
  *
825
- * @param flashlightSkill The flashlight skill that considers sliders.
826
- * @param flashlightSkillWithoutSliders The flashlight skill that doesn't consider sliders.
998
+ * Misses are ignored because they are usually due to misaiming.
999
+ *
1000
+ * Greats and oks are assumed to follow a normal distribution, whereas mehs are assumed to follow a uniform distribution.
827
1001
  */
828
- private postCalculateFlashlight;
1002
+ private calculateDeviation;
829
1003
  /**
830
- * Called after visual skill calculation.
1004
+ * Calculates a multiplier for tap to account for improper tapping based on the deviation and tap difficulty.
831
1005
  *
832
- * @param visualSkillWithSliders The visual skill that considers sliders.
833
- * @param visualSkillWithoutSliders The visual skill that doesn't consider sliders.
1006
+ * [Graph](https://www.desmos.com/calculator/z5l9ebrwpi)
834
1007
  */
835
- private postCalculateVisual;
836
- }
837
-
838
- /**
839
- * Represents the skill required to memorize and hit every object in a beatmap with the Flashlight mod enabled.
840
- */
841
- declare class DroidFlashlight extends DroidSkill {
842
- protected readonly skillMultiplier: number;
843
- protected readonly strainDecayBase: number;
844
- protected readonly reducedSectionCount: number;
845
- protected readonly reducedSectionBaseline: number;
846
- protected readonly starsPerDouble: number;
847
- private readonly isHidden;
848
- private readonly withSliders;
849
- constructor(mods: Mod[], withSliders: boolean);
1008
+ private calculateTapHighDeviationNerf;
1009
+ private getHitWindow;
1010
+ private calculateEstimatedSliderBreaks;
1011
+ private calculateMaximumComboBasedMissCount;
850
1012
  /**
851
- * @param current The hitobject to calculate.
1013
+ * Calculates the amount of misses + sliderbreaks from combo.
852
1014
  */
853
- protected strainValueAt(current: DifficultyHitObject): number;
854
- protected saveToHitObject(current: DifficultyHitObject): void;
1015
+ private calculateComboBasedEstimatedMissCount;
855
1016
  }
856
1017
 
857
1018
  /**
858
- * An evaluator for calculating flashlight skill.
859
- *
860
- * This class should be considered an "evaluating" class and not persisted.
1019
+ * An evaluator for calculating osu!droid agility aim difficulty.
861
1020
  */
862
- declare abstract class FlashlightEvaluator {
863
- protected static readonly maxOpacityBonus: number;
864
- protected static readonly hiddenBonus: number;
865
- protected static readonly minVelocity: number;
866
- protected static readonly sliderMultiplier: number;
867
- protected static readonly minAngleMultiplier: number;
1021
+ declare abstract class DroidAgilityEvaluator {
1022
+ /**
1023
+ * Evaluates the difficulty of fast aiming the current object.
1024
+ *
1025
+ * @param current The current object.
1026
+ */
1027
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject): number;
1028
+ private static highBpmBonus;
868
1029
  }
869
1030
 
870
1031
  /**
871
1032
  * An evaluator for calculating osu!droid Flashlight skill.
872
1033
  */
873
- declare abstract class DroidFlashlightEvaluator extends FlashlightEvaluator {
1034
+ declare abstract class DroidFlashlightEvaluator {
1035
+ private static readonly maxOpacityBonus;
1036
+ private static readonly hiddenBonus;
1037
+ private static readonly traceableCircleBonus;
1038
+ private static readonly traceableObjectBonus;
1039
+ private static readonly minVelocity;
1040
+ private static readonly sliderMultiplier;
1041
+ private static readonly minAngleMultiplier;
874
1042
  /**
875
1043
  * Evaluates the difficulty of memorizing and hitting the current object, based on:
876
1044
  *
@@ -881,240 +1049,466 @@ declare abstract class DroidFlashlightEvaluator extends FlashlightEvaluator {
881
1049
  * - and whether Hidden mod is enabled.
882
1050
  *
883
1051
  * @param current The current object.
884
- * @param isHiddenMod Whether the Hidden mod is enabled.
885
- * @param withSliders Whether to take slider difficulty into account.
1052
+ * @param mods The mods used.
886
1053
  */
887
- static evaluateDifficultyOf(current: DifficultyHitObject, isHiddenMod: boolean, withSliders: boolean): number;
1054
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, mods: ModMap): number;
888
1055
  }
889
1056
 
890
1057
  /**
891
- * Represents options for performance calculation.
1058
+ * An evaluator for calculating osu!droid flow aim difficulty.
892
1059
  */
893
- interface PerformanceCalculationOptions {
1060
+ declare abstract class DroidFlowAimEvaluator {
1061
+ private static readonly velocityChangeMultiplier;
1062
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, withSliders: boolean): number;
1063
+ private static calculateOverlapFactor;
1064
+ }
1065
+
1066
+ /**
1067
+ * Evaluator for reading difficulty in osu!droid.
1068
+ */
1069
+ declare abstract class DroidReadingEvaluator {
1070
+ private static readonly readingWindowSize;
1071
+ private static readonly hiddenMultiplier;
1072
+ private static readonly traceableMultiplier;
1073
+ private static readonly densityMultiplier;
1074
+ private static readonly densityDifficultyBase;
1075
+ private static readonly preemptBalancingFactor;
1076
+ private static readonly preemptStartingPoint;
1077
+ private static readonly minimumAngleRelevancyTime;
1078
+ private static readonly maximumAngleRelevancyTime;
894
1079
  /**
895
- * The maximum combo achieved in the score.
1080
+ * Evaluates the difficulty of reading the object.
896
1081
  */
897
- combo?: number;
1082
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, mods: ModMap): number;
898
1083
  /**
899
- * The accuracy achieved in the score.
1084
+ * Calculates the density difficulty of the current object and how hard it is to aim it because of it based on:
1085
+ *
1086
+ * - cursor velocity to the current object,
1087
+ * - how many times the current object's angle was repeated,
1088
+ * - density of objects visible when the current object appears, and
1089
+ * - density of objects visible when the current object needs to be clicked.
900
1090
  */
901
- accPercent?: Accuracy | number;
1091
+ private static calculateDensityDifficulty;
902
1092
  /**
903
- * The amount of misses achieved in the score.
1093
+ * Calculates the difficulty of aiming the current object when the approach rate is very high based on:
1094
+ *
1095
+ * - cursor velocity to the current object,
1096
+ * - how many times the current object's angle was repeated, and
1097
+ * - how many milliseconds elapse between the approach circle appearing and touching the inner circle.
904
1098
  */
905
- miss?: number;
1099
+ private static calculatePreemptDifficulty;
906
1100
  /**
907
- * The tap penalty to apply for penalized scores. Only used when using `DroidPerformanceCalculator`.
1101
+ * Calculates the difficulty of aiming the current object when the Hidden mod is active based on:
1102
+ *
1103
+ * - cursor velocity to the current object,
1104
+ * - time the current object spends invisible,
1105
+ * - density of objects visible when the current object appears,
1106
+ * - density of objects visible when the current object needs to be clicked,
1107
+ * - how many times the current object's angle was repeated, and
1108
+ * - if the current object is perfectly stacked to the previous one.
908
1109
  */
909
- tapPenalty?: number;
1110
+ private static calculateHiddenDifficulty;
1111
+ private static calculateTraceableDifficulty;
1112
+ private static getPastObjectDifficultyInfluence;
910
1113
  /**
911
- * The aim slider cheese penalty to apply for penalized scores. Only used when using `DroidPerformanceCalculator`.
1114
+ * Returns a list of objects that are visible on screen at the point in time the current object becomes visible.
912
1115
  */
913
- aimSliderCheesePenalty?: number;
1116
+ private static retrievePastVisibleObjects;
914
1117
  /**
915
- * The flashlight slider cheese penalty to apply for penalized scores. Only used when using `DroidPerformanceCalculator`.
1118
+ * Returns the density of objects visible at the point in time the current object needs to be clicked capped by the reading window.
916
1119
  */
917
- flashlightSliderCheesePenalty?: number;
1120
+ private static retrieveCurrentVisibleObjectDensity;
918
1121
  /**
919
- * The visual slider cheese penalty to apply for penalized scores. Only used when using `DroidPerformanceCalculator`.
1122
+ * Returns a factor of how often the current object's angle has been repeated in a certain time frame.
1123
+ * It does this by checking the difference in angle between current and past objects and sums them based on a range of similarity.
1124
+ * https://www.desmos.com/calculator/eb057a4822
920
1125
  */
921
- visualSliderCheesePenalty?: number;
1126
+ private static getConstantAngleNerfFactor;
1127
+ /**
1128
+ * Returns a nerfing factor for when objects are very distant in time, affecting reading less.
1129
+ */
1130
+ private static getTimeNerfFactor;
1131
+ private static highBpmBonus;
922
1132
  }
923
1133
 
924
1134
  /**
925
- * The base class of performance calculators.
1135
+ * An evaluator for calculating osu!droid Rhythm skill.
926
1136
  */
927
- declare abstract class PerformanceCalculator {
1137
+ declare abstract class DroidRhythmEvaluator {
1138
+ private static readonly historyTimeMax;
1139
+ private static readonly historyObjectsMax;
1140
+ private static readonly rhythmOverallMultiplier;
1141
+ private static readonly rhythmRatioMultiplier;
928
1142
  /**
929
- * The overall performance value.
1143
+ * Calculates a rhythm multiplier for the difficulty of the tap associated
1144
+ * with historic data of the current object.
1145
+ *
1146
+ * @param current The current object.
1147
+ * @param useSliderAccuracy Whether to use slider accuracy.
930
1148
  */
931
- total: number;
1149
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, useSliderAccuracy: boolean): number;
1150
+ private static getEffectiveRatio;
1151
+ }
1152
+
1153
+ /**
1154
+ * An evaluator for calculating osu!droid snap aim difficulty.
1155
+ */
1156
+ declare abstract class DroidSnapAimEvaluator {
1157
+ private static readonly wideAngleMultiplier;
1158
+ private static readonly acuteAngleMultiplier;
1159
+ private static readonly sliderMultiplier;
1160
+ private static readonly velocityChangeMultiplier;
1161
+ private static readonly wiggleMultiplier;
1162
+ private static readonly angleRepetitionNoteLimit;
1163
+ private static readonly maximumRepetitionNerf;
1164
+ private static readonly maximumVectorInfluence;
932
1165
  /**
933
- * The calculated accuracy.
1166
+ * Evaluates the difficulty of aiming the current object, based on:
1167
+ *
1168
+ * - cursor velocity to the current object,
1169
+ * - angle difficulty,
1170
+ * - sharp velocity increases,
1171
+ * - and slider difficulty.
1172
+ *
1173
+ * @param current The current object.
1174
+ * @param withSliders Whether to take slider difficulty into account.
934
1175
  */
935
- computedAccuracy: Accuracy;
1176
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, withSliders: boolean): number;
1177
+ private static calculateWideAngleAcuteness;
1178
+ static calculateAcuteAngleAcuteness(angle: number): number;
1179
+ private static highBpmBonus;
1180
+ private static calculateVectorAngleRepetition;
1181
+ }
1182
+
1183
+ /**
1184
+ * An evaluator for calculating osu!droid tap skill.
1185
+ */
1186
+ declare abstract class DroidTapEvaluator {
1187
+ private static readonly minSpeedBonus;
936
1188
  /**
937
- * The difficulty attributes that is being calculated.
1189
+ * Evaluates the difficulty of tapping the current object, based on:
1190
+ *
1191
+ * - time between pressing the previous and current object,
1192
+ * - distance between those objects,
1193
+ * - how easily they can be cheesed,
1194
+ * - and the strain time cap.
1195
+ *
1196
+ * @param current The current object.
1197
+ * @param considerCheesability Whether to consider cheesability.
938
1198
  */
939
- abstract readonly difficultyAttributes: DifficultyAttributes;
1199
+ static evaluateDifficultyOf(current: DroidDifficultyHitObject, considerCheesability: boolean): number;
1200
+ private static highBpmBonus;
1201
+ }
1202
+
1203
+ /**
1204
+ * Represents an osu!standard hit object with difficulty calculation values.
1205
+ */
1206
+ declare class OsuDifficultyHitObject extends DifficultyHitObject {
940
1207
  /**
941
- * Penalty for combo breaks.
1208
+ * The speed strain generated by the hitobject.
942
1209
  */
943
- protected comboPenalty: number;
1210
+ speedStrain: number;
1211
+ readonly normalizedRadius = 50;
1212
+ get smallCircleBonus(): number;
1213
+ get overallDifficulty(): number;
1214
+ protected readonly mode = Modes.Osu;
1215
+ }
1216
+
1217
+ /**
1218
+ * An evaluator for calculating osu!standard agility aim difficulty.
1219
+ */
1220
+ declare abstract class OsuAgilityEvaluator {
944
1221
  /**
945
- * The global multiplier to be applied to the final performance value.
1222
+ * Evaluates the difficulty of fast aiming the current object.
946
1223
  *
947
- * This is being adjusted to keep the final value scaled around what it used to be when changing things.
948
- */
949
- protected abstract finalMultiplier: number;
950
- /**
951
- * The gamemode to calculate for.
1224
+ * @param current The current object.
952
1225
  */
953
- protected abstract readonly mode: Modes;
1226
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject): number;
1227
+ private static highBpmBonus;
1228
+ }
1229
+
1230
+ /**
1231
+ * An evaluator for calculating osu!standard Flashlight skill.
1232
+ */
1233
+ declare abstract class OsuFlashlightEvaluator {
1234
+ private static readonly maxOpacityBonus;
1235
+ private static readonly hiddenBonus;
1236
+ private static readonly minVelocity;
1237
+ private static readonly sliderMultiplier;
1238
+ private static readonly minAngleMultiplier;
954
1239
  /**
955
- * The amount of misses that are filtered out from sliderbreaks.
1240
+ * Evaluates the difficulty of memorizing and hitting the current object, based on:
1241
+ *
1242
+ * - distance between a number of previous objects and the current object,
1243
+ * - the visual opacity of the current object,
1244
+ * - the angle made by the current object,
1245
+ * - length and speed of the current object (for sliders),
1246
+ * - and whether Hidden mod is enabled.
1247
+ *
1248
+ * @param current The current object.
1249
+ * @param mods The mods used.
956
1250
  */
957
- protected effectiveMissCount: number;
1251
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject, mods: ModMap): number;
1252
+ }
1253
+
1254
+ /**
1255
+ * An evaluator for calculating osu!standard flow aim difficulty.
1256
+ */
1257
+ declare abstract class OsuFlowAimEvaluator {
1258
+ private static readonly velocityChangeMultiplier;
1259
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject, withSliders: boolean): number;
1260
+ private static calculateOverlapFactor;
1261
+ }
1262
+
1263
+ /**
1264
+ * Evaluator for reading difficulty in osu!standard.
1265
+ */
1266
+ declare abstract class OsuReadingEvaluator {
1267
+ private static readonly readingWindowSize;
1268
+ private static readonly hiddenMultiplier;
1269
+ private static readonly densityMultiplier;
1270
+ private static readonly densityDifficultyBase;
1271
+ private static readonly preemptBalancingFactor;
1272
+ private static readonly preemptStartingPoint;
1273
+ private static readonly minimumAngleRelevancyTime;
1274
+ private static readonly maximumAngleRelevancyTime;
958
1275
  /**
959
- * Nerf factor used for nerfing beatmaps with very likely dropped sliderends.
1276
+ * Evaluates the difficulty of reading the object.
960
1277
  */
961
- protected sliderNerfFactor: number;
1278
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject, mods: ModMap): number;
962
1279
  /**
963
- * Calculates the performance points of the beatmap.
1280
+ * Calculates the density difficulty of the current object and how hard it is to aim it because of it based on:
964
1281
  *
965
- * @param options Options for performance calculation.
966
- * @returns The current instance.
1282
+ * - cursor velocity to the current object,
1283
+ * - how many times the current object's angle was repeated,
1284
+ * - density of objects visible when the current object appears, and
1285
+ * - density of objects visible when the current object needs to be clicked.
967
1286
  */
968
- calculate(options?: PerformanceCalculationOptions): this;
1287
+ private static calculateDensityDifficulty;
969
1288
  /**
970
- * Returns a string representative of the class.
1289
+ * Calculates the difficulty of aiming the current object when the approach rate is very high based on:
1290
+ *
1291
+ * - cursor velocity to the current object,
1292
+ * - how many times the current object's angle was repeated, and
1293
+ * - how many milliseconds elapse between the approach circle appearing and touching the inner circle.
971
1294
  */
972
- abstract toString(): string;
1295
+ private static calculatePreemptDifficulty;
973
1296
  /**
974
- * Calculates all values that will be used for calculating the total
975
- * performance value of the beatmap and stores them in this instance.
1297
+ * Calculates the difficulty of aiming the current object when the Hidden mod is active based on:
1298
+ *
1299
+ * - cursor velocity to the current object,
1300
+ * - time the current object spends invisible,
1301
+ * - density of objects visible when the current object appears,
1302
+ * - density of objects visible when the current object needs to be clicked,
1303
+ * - how many times the current object's angle was repeated, and
1304
+ * - if the current object is perfectly stacked to the previous one.
976
1305
  */
977
- protected abstract calculateValues(): void;
1306
+ private static calculateHiddenDifficulty;
1307
+ private static getPastObjectDifficultyInfluence;
978
1308
  /**
979
- * Calculates the total performance value of the beatmap and stores it in this instance.
1309
+ * Returns a list of objects that are visible on screen at the point in time the current object becomes visible.
980
1310
  */
981
- protected abstract calculateTotalValue(): void;
1311
+ private static retrievePastVisibleObjects;
982
1312
  /**
983
- * The total hits that can be done in the beatmap.
1313
+ * Returns the density of objects visible at the point in time the current object needs to be clicked capped by the reading window.
984
1314
  */
985
- protected get totalHits(): number;
1315
+ private static retrieveCurrentVisibleObjectDensity;
986
1316
  /**
987
- * The total hits that were successfully done.
1317
+ * Returns a factor of how often the current object's angle has been repeated in a certain time frame.
1318
+ * It does this by checking the difference in angle between current and past objects and sums them based on a range of similarity.
1319
+ * https://www.desmos.com/calculator/eb057a4822
988
1320
  */
989
- protected get totalSuccessfulHits(): number;
1321
+ private static getConstantAngleNerfFactor;
990
1322
  /**
991
- * Calculates the base performance value of a star rating.
1323
+ * Returns a nerfing factor for when objects are very distant in time, affecting reading less.
992
1324
  */
993
- protected baseValue(stars: number): number;
1325
+ private static getTimeNerfFactor;
1326
+ private static highBpmBonus;
1327
+ }
1328
+
1329
+ /**
1330
+ * An evaluator for calculating osu!standard Rhythm skill.
1331
+ */
1332
+ declare abstract class OsuRhythmEvaluator {
1333
+ private static readonly historyTimeMax;
1334
+ private static readonly historyObjectsMax;
1335
+ private static readonly rhythmOverallMultiplier;
1336
+ private static readonly rhythmRatioMultiplier;
994
1337
  /**
995
- * Processes given options for usage in performance calculation.
1338
+ * Calculates a rhythm multiplier for the difficulty of the tap associated
1339
+ * with historic data of the current object.
996
1340
  *
997
- * @param options Options for performance calculation.
1341
+ * @param current The current object.
998
1342
  */
999
- protected handleOptions(options?: PerformanceCalculationOptions): void;
1343
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject): number;
1344
+ private static getEffectiveRatio;
1345
+ }
1346
+
1347
+ /**
1348
+ * An evaluator for calculating osu!standard snap aim difficulty.
1349
+ */
1350
+ declare abstract class OsuSnapAimEvaluator {
1351
+ private static readonly wideAngleMultiplier;
1352
+ private static readonly acuteAngleMultiplier;
1353
+ private static readonly sliderMultiplier;
1354
+ private static readonly velocityChangeMultiplier;
1355
+ private static readonly wiggleMultiplier;
1356
+ private static readonly angleRepetitionNoteLimit;
1357
+ private static readonly maximumRepetitionNerf;
1358
+ private static readonly maximumVectorInfluence;
1000
1359
  /**
1001
- * Calculates the amount of misses + sliderbreaks from combo.
1360
+ * Evaluates the difficulty of aiming the current object, based on:
1361
+ *
1362
+ * - cursor velocity to the current object,
1363
+ * - angle difficulty,
1364
+ * - sharp velocity increases,
1365
+ * - and slider difficulty.
1366
+ *
1367
+ * @param current The current object.
1368
+ * @param withSliders Whether to take slider difficulty into account.
1002
1369
  */
1003
- private calculateEffectiveMissCount;
1370
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject, withSliders: boolean): number;
1371
+ private static calculateWideAngleAcuteness;
1372
+ static calculateAcuteAngleAcuteness(angle: number): number;
1373
+ private static highBpmBonus;
1374
+ private static calculateVectorAngleRepetition;
1004
1375
  }
1005
1376
 
1006
1377
  /**
1007
- * A performance points calculator that calculates performance points for osu!droid gamemode.
1378
+ * An evaluator for calculating osu!standard speed skill.
1008
1379
  */
1009
- declare class DroidPerformanceCalculator extends PerformanceCalculator {
1380
+ declare abstract class OsuSpeedEvaluator {
1381
+ private static readonly minSpeedBonus;
1010
1382
  /**
1011
- * The aim performance value.
1383
+ * Evaluates the difficulty of tapping the current object, based on:
1384
+ *
1385
+ * - time between pressing the previous and current object,
1386
+ * - and how easily they can be cheesed.
1387
+ *
1388
+ * @param current The current object.
1012
1389
  */
1013
- aim: number;
1390
+ static evaluateDifficultyOf(current: OsuDifficultyHitObject): number;
1391
+ private static highBpmBonus;
1392
+ }
1393
+
1394
+ /**
1395
+ * Holds data that can be used to calculate osu!standard performance points.
1396
+ */
1397
+ interface IOsuDifficultyAttributes extends IDifficultyAttributes {
1014
1398
  /**
1015
- * The tap performance value.
1399
+ * The perceived approach rate **exclusive** of rate-adjusting mods (DT/HT/etc).
1400
+ *
1401
+ * Rate-adjusting mods don't directly affect the approach rate difficulty value, but have a perceived effect as a result of adjusting audio timing.
1016
1402
  */
1017
- tap: number;
1403
+ approachRate: number;
1018
1404
  /**
1019
- * The accuracy performance value.
1405
+ * The health drain rate of the beatmap.
1020
1406
  */
1021
- accuracy: number;
1407
+ drainRate: number;
1022
1408
  /**
1023
- * The flashlight performance value.
1409
+ * The difficulty corresponding to the speed skill.
1024
1410
  */
1025
- flashlight: number;
1411
+ speedDifficulty: number;
1026
1412
  /**
1027
- * The visual performance value.
1413
+ * The amount of strains that are considered difficult with respect to the speed skill.
1028
1414
  */
1029
- visual: number;
1415
+ speedDifficultStrainCount: number;
1030
1416
  /**
1031
- * The penalty used to penalize the tap performance value.
1417
+ * Describes how much of {@link aimDifficultStrainCount} is contributed to by circles or sliders.
1032
1418
  *
1033
- * Can be properly obtained by analyzing the replay associated with the score.
1034
- */
1035
- get tapPenalty(): number;
1036
- /**
1037
- * The estimated deviation of the score.
1038
- */
1039
- get deviation(): number;
1040
- /**
1041
- * The estimated tap deviation of the score.
1042
- */
1043
- get tapDeviation(): number;
1044
- /**
1045
- * The penalty used to penalize the aim performance value.
1419
+ * A value closer to 0 indicates most of {@link aimDifficultStrainCount} is contributed by circles.
1046
1420
  *
1047
- * Can be properly obtained by analyzing the replay associated with the score.
1421
+ * A value closer to infinity indicates most of {@link aimDifficultStrainCount} is contributed by sliders.
1048
1422
  */
1049
- get aimSliderCheesePenalty(): number;
1423
+ aimTopWeightedSliderFactor: number;
1050
1424
  /**
1051
- * The penalty used to penalize the flashlight performance value.
1425
+ * Describes how much of {@link speedDifficultStrainCount} is contributed to by circles or sliders.
1052
1426
  *
1053
- * Can be properly obtained by analyzing the replay associated with the score.
1054
- */
1055
- get flashlightSliderCheesePenalty(): number;
1056
- /**
1057
- * The penalty used to penalize the visual performance value.
1427
+ * A value closer to 0 indicates most of {@link speedDifficultStrainCount} is contributed by circles.
1058
1428
  *
1059
- * Can be properly obtained by analyzing the replay associated with the score.
1429
+ * A value closer to infinity indicates most of {@link speedDifficultStrainCount} is contributed by sliders.
1060
1430
  */
1061
- get visualSliderCheesePenalty(): number;
1062
- readonly difficultyAttributes: DroidDifficultyAttributes;
1063
- protected finalMultiplier: number;
1064
- protected readonly mode: Modes;
1065
- private _aimSliderCheesePenalty;
1066
- private _flashlightSliderCheesePenalty;
1067
- private _visualSliderCheesePenalty;
1068
- private _tapPenalty;
1069
- private _deviation;
1070
- private _tapDeviation;
1431
+ speedTopWeightedSliderFactor: number;
1432
+ }
1433
+
1434
+ /**
1435
+ * Holds data that can be used to calculate osu!standard performance points.
1436
+ */
1437
+ declare class OsuDifficultyAttributes extends DifficultyAttributes implements IOsuDifficultyAttributes {
1438
+ approachRate: number;
1439
+ drainRate: number;
1440
+ speedDifficulty: number;
1441
+ speedDifficultStrainCount: number;
1442
+ speedTopWeightedSliderFactor: number;
1443
+ constructor(cacheableAttributes?: CacheableDifficultyAttributes<IOsuDifficultyAttributes>);
1444
+ toString(): string;
1445
+ }
1446
+
1447
+ /**
1448
+ * A difficulty calculator for osu!standard gamemode.
1449
+ */
1450
+ declare class OsuDifficultyCalculator extends DifficultyCalculator<OsuPlayableBeatmap, OsuDifficultyHitObject, OsuDifficultyAttributes> {
1451
+ constructor();
1452
+ retainDifficultyAdjustmentMods(mods: Mod[]): Mod[];
1453
+ protected createDifficultyAttributes(beatmap: Beatmap, playableBeatmap: PlayableBeatmap, skills: Skill[]): OsuDifficultyAttributes;
1454
+ protected createPlayableBeatmap(beatmap: Beatmap, mods?: ModMap): OsuPlayableBeatmap;
1455
+ protected createDifficultyHitObjects(beatmap: OsuPlayableBeatmap): OsuDifficultyHitObject[];
1456
+ protected createSkills(beatmap: OsuPlayableBeatmap): Skill[];
1457
+ protected createStrainPeakSkills(beatmap: OsuPlayableBeatmap): (Skill & IHasPeakDifficulty)[];
1458
+ private calculateAimDifficultyRating;
1459
+ private calculateDifficultyRating;
1460
+ private calculateStarRating;
1461
+ static sumCognitionDifficulty(reading: number, flashlight: number): number;
1462
+ }
1463
+
1464
+ /**
1465
+ * Represents the calculated performance of a score in osu!.
1466
+ */
1467
+ interface IOsuPerformanceAttributes extends IPerformanceAttributes {
1071
1468
  /**
1072
- * @param difficultyAttributes The difficulty attributes to calculate.
1469
+ * The aim performance points.
1073
1470
  */
1074
- constructor(difficultyAttributes: DroidDifficultyAttributes);
1471
+ aim: number;
1075
1472
  /**
1076
- * Applies a tap penalty value to this calculator.
1077
- *
1078
- * The tap and total performance value will be recalculated afterwards.
1079
- *
1080
- * @param value The tap penalty value. Must be greater than or equal to 1.
1473
+ * The speed performance points.
1081
1474
  */
1082
- applyTapPenalty(value: number): void;
1475
+ speed: number;
1083
1476
  /**
1084
- * Applies an aim slider cheese penalty value to this calculator.
1085
- *
1086
- * The aim and total performance value will be recalculated afterwards.
1087
- *
1088
- * @param value The slider cheese penalty value. Must be between than 0 (exclusive) and 1 (inclusive).
1477
+ * The accuracy performance points.
1089
1478
  */
1090
- applyAimSliderCheesePenalty(value: number): void;
1479
+ accuracy: number;
1091
1480
  /**
1092
- * Applies a flashlight slider cheese penalty value to this calculator.
1093
- *
1094
- * The flashlight and total performance value will be recalculated afterwards.
1095
- *
1096
- * @param value The slider cheese penalty value. Must be between 0 (exclusive) and 1 (inclusive).
1481
+ * The flashlight performance points.
1097
1482
  */
1098
- applyFlashlightSliderCheesePenalty(value: number): void;
1483
+ flashlight: number;
1099
1484
  /**
1100
- * Applies a visual slider cheese penalty value to this calculator.
1101
- *
1102
- * The visual and total performance value will be recalculated afterwards.
1103
- *
1104
- * @param value The slider cheese penalty value. Must be between 0 (exclusive) and 1 (inclusive).
1485
+ * The reading performance points.
1105
1486
  */
1106
- applyVisualSliderCheesePenalty(value: number): void;
1107
- protected calculateValues(): void;
1108
- protected calculateTotalValue(): void;
1109
- protected handleOptions(options?: PerformanceCalculationOptions): void;
1487
+ reading: number;
1488
+ }
1489
+
1490
+ /**
1491
+ * A performance points calculator that calculates performance points for osu!standard gamemode.
1492
+ */
1493
+ declare class OsuPerformanceCalculator extends PerformanceCalculator<IOsuDifficultyAttributes, IOsuPerformanceAttributes> {
1494
+ static readonly finalMultiplier = 1.12;
1495
+ static readonly normExponent = 1.1;
1496
+ private greatWindow;
1497
+ private okWindow;
1498
+ private mehWindow;
1499
+ private approachRate;
1500
+ private overallDifficulty;
1501
+ private _effectiveMissCount;
1502
+ private speedDeviation;
1503
+ protected createPerformanceAttributes(): IOsuPerformanceAttributes;
1110
1504
  /**
1111
1505
  * Calculates the aim performance value of the beatmap.
1112
1506
  */
1113
1507
  private calculateAimValue;
1114
1508
  /**
1115
- * Calculates the tap performance value of the beatmap.
1509
+ * Calculates the speed performance value of the beatmap.
1116
1510
  */
1117
- private calculateTapValue;
1511
+ private calculateSpeedValue;
1118
1512
  /**
1119
1513
  * Calculates the accuracy performance value of the beatmap.
1120
1514
  */
@@ -1124,467 +1518,581 @@ declare class DroidPerformanceCalculator extends PerformanceCalculator {
1124
1518
  */
1125
1519
  private calculateFlashlightValue;
1126
1520
  /**
1127
- * Calculates the visual performance value of the beatmap.
1521
+ * Calculates the reading performance value of the beatmap.
1128
1522
  */
1129
- private calculateVisualValue;
1523
+ private calculateReadingValue;
1130
1524
  /**
1131
- * Estimates the player's tap deviation based on the OD, number of circles and sliders,
1132
- * and number of 300s, 100s, 50s, and misses, assuming the player's mean hit error is 0.
1525
+ * Calculates a strain-based miss penalty.
1133
1526
  *
1134
- * The estimation is consistent in that two SS scores on the same map
1135
- * with the same settings will always return the same deviation.
1527
+ * Strain-based miss penalty assumes that a player will miss on the hardest parts of a map,
1528
+ * so we use the amount of relatively difficult sections to adjust miss penalty
1529
+ * to make it more punishing on maps with lower amount of hard sections.
1530
+ */
1531
+ private calculateMissPenalty;
1532
+ /**
1533
+ * Estimates a player's deviation on speed notes using {@link calculateDeviation}, assuming worst-case.
1136
1534
  *
1137
- * Sliders are treated as circles with a 50 hit window.
1535
+ * Treats all speed notes as hit circles.
1536
+ */
1537
+ private calculateSpeedDeviation;
1538
+ /**
1539
+ * Estimates the player's tap deviation based on the OD, given number of greats, oks, mehs and misses,
1540
+ * assuming the player's mean hit error is 0. The estimation is consistent in that two SS scores on the
1541
+ * same map with the same settings will always return the same deviation.
1138
1542
  *
1139
- * Misses are ignored because they are usually due to misaiming, and 50s
1140
- * are grouped with 100s since they are usually due to misreading.
1543
+ * Misses are ignored because they are usually due to misaiming.
1141
1544
  *
1142
- * Inaccuracies are capped to the number of circles in the map.
1545
+ * Greats and oks are assumed to follow a normal distribution, whereas mehs are assumed to follow a uniform distribution.
1143
1546
  */
1144
1547
  private calculateDeviation;
1145
1548
  /**
1146
- * Does the same as {@link calculateDeviation}, but only for notes and inaccuracies that are relevant to tap difficulty.
1549
+ * Calculates multiplier for speed to account for improper tapping based on the deviation and speed difficulty.
1147
1550
  *
1148
- * Treats all difficult speed notes as circles, so this method can sometimes return a lower deviation than {@link calculateDeviation}.
1149
- * This is fine though, since this method is only used to scale tap pp.
1551
+ * [Graph](https://www.desmos.com/calculator/dmogdhzofn)
1150
1552
  */
1151
- private calculateTapDeviation;
1152
- toString(): string;
1153
- }
1154
-
1155
- /**
1156
- * Represents the skill required to properly follow a beatmap's rhythm.
1157
- */
1158
- declare class DroidRhythm extends DroidSkill {
1159
- protected readonly skillMultiplier: number;
1160
- protected readonly reducedSectionCount: number;
1161
- protected readonly reducedSectionBaseline: number;
1162
- protected readonly strainDecayBase: number;
1163
- protected readonly starsPerDouble: number;
1164
- private currentRhythm;
1165
- private readonly hitWindow;
1166
- constructor(mods: Mod[], overallDifficulty: number);
1167
- protected strainValueAt(current: DifficultyHitObject): number;
1168
- protected saveToHitObject(current: DifficultyHitObject): void;
1553
+ private calculateSpeedHighDeviationNerf;
1554
+ private calculateEstimatedSliderBreaks;
1555
+ /**
1556
+ * Calculates the amount of misses + sliderbreaks from combo.
1557
+ */
1558
+ private calculateComboBasedEstimatedMissCount;
1559
+ private calculateTraceableBonus;
1560
+ private calculateRateAdjustedApproachRate;
1169
1561
  }
1170
1562
 
1171
1563
  /**
1172
- * An evaluator for calculating rhythm skill.
1173
- *
1174
- * This class should be considered an "evaluating" class and not persisted.
1564
+ * Data class for variable length strain.
1175
1565
  */
1176
- declare abstract class RhythmEvaluator {
1177
- protected static readonly rhythmMultiplier: number;
1178
- protected static readonly historyTimeMax: number;
1566
+ declare class StrainPeak {
1567
+ readonly value: number;
1568
+ readonly sectionLength: number;
1569
+ constructor(value: number, sectionLength: number);
1570
+ compareTo(other: StrainPeak): number;
1179
1571
  }
1180
1572
 
1181
1573
  /**
1182
- * An evaluator for calculating osu!droid Rhythm skill.
1574
+ * A skill that evaluates strain over a variable length of time. A new strain peak is created for every
1575
+ * {@link DifficultyHitObject}.
1183
1576
  */
1184
- declare abstract class DroidRhythmEvaluator extends RhythmEvaluator {
1577
+ declare abstract class VariableLengthStrainSkill extends Skill implements IHasPeakDifficulty {
1185
1578
  /**
1186
- * Calculates a rhythm multiplier for the difficulty of the tap associated
1187
- * with historic data of the current object.
1579
+ * The weight by which each strain value decays.
1580
+ */
1581
+ protected readonly decayWeight: number;
1582
+ /**
1583
+ * The maximum length of a strain section, in milliseconds.
1584
+ */
1585
+ protected readonly maxSectionLength: number;
1586
+ /**
1587
+ * The number of {@link maxSectionLength} sections calculated such that enough of the difficulty value is preserved.
1188
1588
  *
1189
- * @param current The current object.
1190
- * @param greatWindow The great hit window of the current object.
1589
+ * This should be overridden if strains are ever used outside of {@link difficultyValue}, or if {@link difficultyValue}
1590
+ * is overridden to not use the default geometric sum.
1591
+ *
1592
+ * This should be removed in the future when a better memory-saving technique is implemented.
1191
1593
  */
1192
- static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number): number;
1193
- }
1194
-
1195
- /**
1196
- * Represents the skill required to press keys or tap with regards to keeping up with the speed at which objects need to be hit.
1197
- */
1198
- declare class DroidTap extends DroidSkill {
1199
- protected readonly skillMultiplier: number;
1200
- protected readonly reducedSectionCount: number;
1201
- protected readonly reducedSectionBaseline: number;
1202
- protected readonly strainDecayBase: number;
1203
- protected readonly starsPerDouble: number;
1204
- private currentTapStrain;
1205
- private currentOriginalTapStrain;
1206
- private readonly greatWindow;
1207
- constructor(mods: Mod[], overallDifficulty: number);
1594
+ protected get maxStoredSections(): number;
1595
+ private currentSectionPeak;
1596
+ private currentSectionBegin;
1597
+ private currentSectionEnd;
1598
+ private totalLength;
1599
+ private readonly strainPeaks;
1208
1600
  /**
1209
- * @param current The hitobject to calculate.
1601
+ * The peaks of this skill in chronological order, used for graphing purposes.
1602
+ *
1603
+ * Unlike {@link strainPeaks}, this is never trimmed, as it does not contribute to {@link difficultyValue}.
1210
1604
  */
1211
- protected strainValueAt(current: DifficultyHitObject): number;
1605
+ private readonly chronologicalPeaks;
1606
+ get peaks(): readonly TimedStrainPeak[];
1212
1607
  /**
1213
- * @param current The hitobject to save to.
1608
+ * Stores previous strains so that, if a difficult {@link DifficultyHitObject} is followed by an easier
1609
+ * {@link DifficultyHitObject}, the difficult one gets a full strain instead of being cut short.
1214
1610
  */
1215
- protected saveToHitObject(current: DifficultyHitObject): void;
1216
- }
1217
-
1218
- /**
1219
- * An evaluator for calculating speed or tap skill.
1220
- *
1221
- * This class should be considered an "evaluating" class and not persisted.
1222
- */
1223
- declare abstract class SpeedEvaluator {
1224
- protected static readonly minSpeedBonus: number;
1225
- }
1226
-
1227
- /**
1228
- * An evaluator for calculating osu!droid tap skill.
1229
- */
1230
- declare abstract class DroidTapEvaluator extends SpeedEvaluator {
1611
+ private readonly queuedStrains;
1612
+ static difficultyToPerformance(difficulty: number): number;
1231
1613
  /**
1232
- * Evaluates the difficulty of tapping the current object, based on:
1614
+ * Obtains the live strain peaks for each {@link maxSectionLength} of the beatmap, including the
1615
+ * peak of the current section.
1616
+ */
1617
+ get currentStrainPeaks(): StrainPeak[];
1618
+ difficultyValue(): number;
1619
+ /**
1620
+ * Returns the number of strains weighed against the top strain.
1233
1621
  *
1234
- * - time between pressing the previous and current object,
1235
- * - distance between those objects,
1236
- * - and how easily they can be cheesed.
1622
+ * The result is scaled by clock rate as it affects the total number of strains.
1623
+ */
1624
+ countTopWeightedStrains(difficultyValue: number): number;
1625
+ protected processInternal(current: DifficultyHitObject): number;
1626
+ /**
1627
+ * Calculates the strain value at the {@link DifficultyHitObject}. This value is calculated with or without respect to
1628
+ * previous {@link DifficultyHitObject}s.
1237
1629
  *
1238
- * @param current The current object.
1239
- * @param greatWindow The great hit window of the current object.
1240
- * @param considerCheesability Whether to consider cheesability.
1630
+ * @param current The {@link DifficultyHitObject} for which the strain value should be calculated.
1241
1631
  */
1242
- static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number, considerCheesability: boolean): number;
1243
- }
1244
-
1245
- /**
1246
- * Represents the skill required to read every object in the map.
1247
- */
1248
- declare class DroidVisual extends DroidSkill {
1249
- protected readonly starsPerDouble: number;
1250
- protected readonly reducedSectionCount: number;
1251
- protected readonly reducedSectionBaseline: number;
1252
- protected readonly skillMultiplier: number;
1253
- protected readonly strainDecayBase: number;
1254
- private readonly isHidden;
1255
- private readonly withSliders;
1256
- constructor(mods: Mod[], withSliders: boolean);
1257
- protected strainValueAt(current: DifficultyHitObject): number;
1258
- protected saveToHitObject(current: DifficultyHitObject): void;
1259
- }
1260
-
1261
- /**
1262
- * An evaluator for calculating osu!droid Visual skill.
1263
- */
1264
- declare abstract class DroidVisualEvaluator {
1632
+ protected abstract strainValueAt(current: DifficultyHitObject): number;
1265
1633
  /**
1266
- * Evaluates the difficulty of reading the current object, based on:
1634
+ * Retrieves the peak strain at a point in time.
1267
1635
  *
1268
- * - note density of the current object,
1269
- * - overlapping factor of the current object,
1270
- * - the preempt time of the current object,
1271
- * - the visual opacity of the current object,
1272
- * - the velocity of the current object if it's a slider,
1273
- * - past objects' velocity if they are sliders,
1274
- * - and whether the Hidden mod is enabled.
1636
+ * @param time The time to retrieve the peak strain at.
1637
+ * @param current The current hit object.
1638
+ * @returns The peak strain.
1639
+ */
1640
+ protected abstract calculateInitialStrain(time: number, current: DifficultyHitObject): number;
1641
+ /**
1642
+ * Fills the space between the end of the current section and the current {@link DifficultyHitObject}, if any.
1275
1643
  *
1276
- * @param current The current object.
1277
- * @param isHiddenMod Whether the Hidden mod is enabled.
1278
- * @param withSliders Whether to take slider difficulty into account.
1644
+ * @param current The current {@link DifficultyHitObject}.
1645
+ */
1646
+ private backfillPeaks;
1647
+ /**
1648
+ * Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty.
1279
1649
  */
1280
- static evaluateDifficultyOf(current: DifficultyHitObject, isHiddenMod: boolean, withSliders: boolean): number;
1650
+ private saveCurrentPeak;
1651
+ private startNewSectionFrom;
1652
+ private addStrainPeakInPlace;
1281
1653
  }
1282
1654
 
1283
1655
  /**
1284
- * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
1285
- * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
1656
+ * Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
1286
1657
  */
1287
- declare abstract class OsuSkill extends StrainSkill {
1658
+ declare class DroidAim extends VariableLengthStrainSkill {
1659
+ private currentStrain;
1660
+ private readonly skillMultiplierSnap;
1661
+ private readonly skillMultiplierAgility;
1662
+ private readonly skillMultiplierFlow;
1663
+ private readonly skillMultiplierTotal;
1664
+ private readonly combinedSnapNormExponent;
1288
1665
  /**
1289
- * The default multiplier applied to the final difficulty value after all other calculations.
1290
- *
1291
- * May be overridden via {@link difficultyMultiplier}.
1666
+ * The number of sections with the highest strains, which the peak strain reductions will apply to.
1667
+ * This is done in order to decrease their impact on the overall difficulty of the beatmap.
1292
1668
  */
1293
- static readonly defaultDifficultyMultiplier: number;
1669
+ private readonly reducedSectionTime;
1294
1670
  /**
1295
- * The final multiplier to be applied to the final difficulty value after all other calculations.
1671
+ * The baseline multiplier applied to the section with the biggest strain.
1296
1672
  */
1297
- protected readonly difficultyMultiplier: number;
1673
+ private readonly reducedStrainBaseline;
1674
+ private readonly sliderStrains;
1675
+ private maxSliderStrain;
1676
+ readonly withSliders: boolean;
1677
+ constructor(mods: ModMap, withSliders: boolean);
1298
1678
  /**
1299
- * The weight by which each strain value decays.
1679
+ * Obtains the amount of sliders that are considered difficult in terms of relative strain.
1300
1680
  */
1301
- protected abstract readonly decayWeight: number;
1302
- difficultyValue(): number;
1303
- }
1304
-
1305
- /**
1306
- * Holds data that can be used to calculate osu!standard performance points.
1307
- */
1308
- interface OsuDifficultyAttributes extends DifficultyAttributes {
1681
+ countDifficultSliders(): number;
1309
1682
  /**
1310
- * The difficulty corresponding to the speed skill.
1683
+ * Obtains the amount of sliders that are considered difficult in terms of relative strain, weighted by consistency.
1684
+ *
1685
+ * @param difficultyValue The final difficulty value.
1311
1686
  */
1312
- speedDifficulty: number;
1687
+ countTopWeightedSliders(difficultyValue: number): number;
1688
+ protected strainValueAt(current: DroidDifficultyHitObject): number;
1689
+ protected calculateInitialStrain(time: number, current: DroidDifficultyHitObject): number;
1690
+ protected saveToHitObject(current: DroidDifficultyHitObject, difficulty: number): void;
1691
+ private calculateAdjustedDifficulty;
1692
+ private calculateTotalValue;
1693
+ /**
1694
+ * Converts the ratio of snap to flow into the probability of snapping or flowing.
1695
+ *
1696
+ * Constraints:
1697
+ * - `P(snap) + P(flow) = 1` (the object is always either snapped or flowed)
1698
+ * - `P(snap) = f(snap / flow)` and `P(flow) = f(flow/snap)` (i.e., snap and flow are symmetric and
1699
+ * reversible). This means `f(x) + f(1/x) = 1`
1700
+ * - `0 <= f(x) <= 1` (cannot have negative or greater than 100% probability of snapping or flowing)
1701
+ *
1702
+ * This logistic function is a solution, which fits nicely with the general idea of interpolation and
1703
+ * provides a tuneable constant.
1704
+ *
1705
+ * @param ratio The ratio.
1706
+ * @returns The probability.
1707
+ */
1708
+ private calculateSnapFlowProbability;
1709
+ difficultyValue(): number;
1710
+ private getReducedStrainPeaks;
1711
+ private strainDecay;
1313
1712
  }
1314
1713
 
1315
1714
  /**
1316
- * A difficulty calculator for osu!standard gamemode.
1715
+ * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
1716
+ * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
1317
1717
  */
1318
- declare class OsuDifficultyCalculator extends DifficultyCalculator {
1718
+ declare abstract class StrainSkill extends Skill implements IHasPeakDifficulty {
1319
1719
  /**
1320
- * The aim star rating of the beatmap.
1720
+ * The number of sections with the highest strains, which the peak strain reductions will apply to.
1721
+ * This is done in order to decrease their impact on the overall difficulty of the map for this skill.
1321
1722
  */
1322
- aim: number;
1723
+ protected readonly reducedSectionCount: number;
1323
1724
  /**
1324
- * The speed star rating of the beatmap.
1725
+ * The baseline multiplier applied to the section with the biggest strain.
1325
1726
  */
1326
- speed: number;
1727
+ protected readonly reducedSectionBaseline: number;
1728
+ protected readonly _objectStrains: number[];
1327
1729
  /**
1328
- * The flashlight star rating of the beatmap.
1730
+ * The strains of hitobjects.
1329
1731
  */
1330
- flashlight: number;
1331
- readonly attributes: OsuDifficultyAttributes;
1332
- protected readonly difficultyMultiplier: number;
1333
- protected readonly mode: Modes;
1732
+ get objectStrains(): readonly number[];
1733
+ protected readonly strainPeaks: number[];
1734
+ private readonly strainPeakTimes;
1735
+ get peaks(): readonly TimedStrainPeak[];
1736
+ private readonly sectionLength;
1737
+ private currentStrain;
1738
+ private currentSectionPeak;
1739
+ private currentSectionEnd;
1334
1740
  /**
1335
- * Calculates the aim star rating of the beatmap and stores it in this instance.
1741
+ * Converts a difficulty value to a performance value.
1742
+ *
1743
+ * @param difficulty The difficulty value to convert.
1744
+ * @returns The performance value.
1336
1745
  */
1337
- calculateAim(): void;
1746
+ static difficultyToPerformance(difficulty: number): number;
1338
1747
  /**
1339
- * Calculates the speed star rating of the beatmap and stores it in this instance.
1748
+ * Obtains the live strain peaks for each {@link sectionLength} of the beatmap, including the peak of the current section.
1340
1749
  */
1341
- calculateSpeed(): void;
1750
+ get currentStrainPeaks(): number[];
1342
1751
  /**
1343
- * Calculates the flashlight star rating of the beatmap and stores it in this instance.
1752
+ * Returns the number of strains weighed against the top strain.
1753
+ *
1754
+ * The result is scaled by clock rate as it affects the total number of strains.
1755
+ *
1756
+ * @param difficultyValue The final difficulty value.
1344
1757
  */
1345
- calculateFlashlight(): void;
1346
- calculateTotal(): void;
1347
- calculateAll(): void;
1758
+ countTopWeightedStrains(difficultyValue: number): number;
1759
+ protected processInternal(current: DifficultyHitObject): number;
1348
1760
  /**
1349
- * Returns a string representative of the class.
1761
+ * Calculates the starting time of a strain section at an object.
1762
+ *
1763
+ * @param current The object at which the strain section starts.
1764
+ * @returns The start time of the strain section.
1350
1765
  */
1351
- toString(): string;
1766
+ protected calculateCurrentSectionStart(current: DifficultyHitObject): number;
1352
1767
  /**
1353
- * Creates skills to be calculated.
1768
+ * Calculates the strain value at a hitobject.
1769
+ *
1770
+ * @param current The hitobject to calculate.
1354
1771
  */
1355
- protected createSkills(): OsuSkill[];
1772
+ protected abstract strainValueAt(current: DifficultyHitObject): number;
1356
1773
  /**
1357
- * Called after aim skill calculation.
1774
+ * Retrieves the peak strain at a point in time.
1358
1775
  *
1359
- * @param aimSkill The aim skill that considers sliders.
1360
- * @param aimSkillWithoutSliders The aim skill that doesn't consider sliders.
1776
+ * @param time The time to retrieve the peak strain at.
1777
+ * @param current The current hit object.
1778
+ * @returns The peak strain.
1779
+ */
1780
+ protected abstract calculateInitialStrain(time: number, current: DifficultyHitObject): number;
1781
+ /**
1782
+ * Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty.
1361
1783
  */
1362
- private postCalculateAim;
1784
+ private saveCurrentPeak;
1363
1785
  /**
1364
- * Called after speed skill calculation.
1786
+ * Sets the initial strain level for a new section.
1365
1787
  *
1366
- * @param speedSkill The speed skill.
1788
+ * @param time The beginning of the new section in milliseconds.
1789
+ * @param current The current hitobject.
1367
1790
  */
1368
- private postCalculateSpeed;
1791
+ private startNewSectionFrom;
1792
+ }
1793
+
1794
+ /**
1795
+ * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
1796
+ * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
1797
+ */
1798
+ declare abstract class DroidSkill extends StrainSkill {
1369
1799
  /**
1370
- * Calculates speed-related attributes.
1800
+ * The bonus multiplier that is given for a sequence of notes of equal difficulty.
1371
1801
  */
1372
- private calculateSpeedAttributes;
1802
+ protected abstract readonly starsPerDouble: number;
1803
+ protected difficulty: number;
1804
+ process(current: DifficultyHitObject): void;
1805
+ difficultyValue(): number;
1373
1806
  /**
1374
- * Called after flashlight skill calculation.
1807
+ * Gets the strain of a hitobject.
1375
1808
  *
1376
- * @param flashlightSkill The flashlight skill.
1809
+ * @param current The hitobject to get the strain from.
1810
+ * @returns The strain of the hitobject.
1377
1811
  */
1378
- private postCalculateFlashlight;
1812
+ protected abstract getObjectStrain(current: DifficultyHitObject): number;
1813
+ protected calculateCurrentSectionStart(current: DifficultyHitObject): number;
1379
1814
  }
1380
1815
 
1381
1816
  /**
1382
- * A difficulty calculator that calculates for both osu!droid and osu!standard gamemode.
1817
+ * Represents the skill required to memorize and hit every object in a beatmap with the Flashlight mod enabled.
1383
1818
  */
1384
- declare class MapStars {
1819
+ declare class DroidFlashlight extends DroidSkill {
1820
+ private readonly totalObjects;
1821
+ protected readonly reducedSectionCount = 0;
1822
+ protected readonly reducedSectionBaseline = 1;
1823
+ protected readonly starsPerDouble = 1;
1824
+ private readonly skillMultiplier;
1825
+ private currentFlashlightStrain;
1826
+ static difficultyToPerformance(difficulty: number): number;
1827
+ constructor(mods: ModMap, totalObjects: number);
1828
+ protected strainValueAt(current: DroidDifficultyHitObject): number;
1829
+ protected calculateInitialStrain(time: number, current: DifficultyHitObject): number;
1830
+ protected getObjectStrain(): number;
1831
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
1832
+ difficultyValue(): number;
1833
+ private calculateAdjustedDifficulty;
1834
+ private strainDecay;
1835
+ }
1836
+
1837
+ /**
1838
+ * A skill that calculates the difficulty of {@link DifficultyHitObject}s using harmonic summation.
1839
+ */
1840
+ declare abstract class HarmonicSkill extends Skill implements IHasPeakDifficulty {
1841
+ private _noteWeightSum;
1842
+ private _objectTimes;
1843
+ /**
1844
+ * The sum of note weights, calculated during summation.
1845
+ *
1846
+ * Required for any calculations that normalizes the difficulty value.
1847
+ */
1848
+ protected get noteWeightSum(): number;
1385
1849
  /**
1386
- * The osu!droid difficulty calculator of the beatmap.
1850
+ * The start times of {@link DifficultyHitObject}s, populated by {@link HarmonicSkill.process}.
1851
+ *
1852
+ * Indices correspond to {@link objectDifficulties}.
1387
1853
  */
1388
- readonly droid: DroidDifficultyCalculator;
1854
+ protected get objectTimes(): readonly number[];
1389
1855
  /**
1390
- * The osu!standard difficulty calculator of the beatmap.
1856
+ * Scaling factor applied as `x / (i + 1)`, where `x` is the skill's {@link harmonicScale} and `i`
1857
+ * is the index of the {@link DifficultyHitObject} being processed.
1858
+ *
1859
+ * A higher value increases the influence of the hardest {@link DifficultyHitObject}s during summation.
1391
1860
  */
1392
- readonly osu: OsuDifficultyCalculator;
1861
+ protected readonly harmonicScale: number;
1393
1862
  /**
1394
- * Constructs this instance and calculates the given beatmap's osu!droid and osu!standard difficulty.
1863
+ * An exponent that controls the rate of which decay increases as the index increases.
1395
1864
  *
1396
- * @param beatmap The beatmap to calculate.
1397
- * @param options Options for the difficulty calculation.
1865
+ * Values closer to 1 decay faster, whilst lower values give more weight to easier {@link DifficultyHitObject}s.
1398
1866
  */
1399
- constructor(beatmap: Beatmap, options?: DifficultyCalculationOptions);
1867
+ protected readonly decayExponent: number;
1868
+ get peaks(): readonly TimedStrainPeak[];
1869
+ static difficultyToPerformance(difficulty: number): number;
1870
+ difficultyValue(): number;
1400
1871
  /**
1401
- * Returns a string representative of the class.
1872
+ * Calculates the amount of object difficulties weighed against the top object difficulty.
1873
+ *
1874
+ * @param difficultyValue The final difficulty value.
1402
1875
  */
1403
- toString(): string;
1404
- }
1405
-
1406
- /**
1407
- * Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
1408
- */
1409
- declare class OsuAim extends OsuSkill {
1410
- protected readonly skillMultiplier: number;
1411
- protected readonly strainDecayBase: number;
1412
- protected readonly reducedSectionCount: number;
1413
- protected readonly reducedSectionBaseline: number;
1414
- protected readonly difficultyMultiplier: number;
1415
- protected readonly decayWeight: number;
1416
- private readonly withSliders;
1417
- constructor(mods: Mod[], withSliders: boolean);
1876
+ countTopWeightedObjectDifficulties(difficultyValue: number): number;
1418
1877
  /**
1419
- * @param current The hitobject to calculate.
1878
+ * Transforms the difficulties of {@link DifficultyHitObject}s before they are summed together.
1879
+ *
1880
+ * This can be used to decrease weight of certain {@link DifficultyHitObject}s based on a skill-specific criteria.
1881
+ *
1882
+ * @param difficulties The difficulties of {@link DifficultyHitObject}s to transform.
1420
1883
  */
1421
- protected strainValueAt(current: DifficultyHitObject): number;
1884
+ protected applyDifficultyTransformation(difficulties: number[]): void;
1885
+ process(current: DifficultyHitObject): void;
1886
+ protected processInternal(current: DifficultyHitObject): number;
1422
1887
  /**
1423
- * @param current The hitobject to save to.
1888
+ * Calculates the difficulty value of a {@link DifficultyHitObject}. This value is calculated with or without respect to previous objects.
1889
+ *
1890
+ * @param current The {@link DifficultyHitObject} for which the difficulty value should be calculated.
1424
1891
  */
1425
- protected saveToHitObject(current: DifficultyHitObject): void;
1892
+ protected abstract objectDifficultyOf(current: DifficultyHitObject): number;
1426
1893
  }
1427
1894
 
1428
1895
  /**
1429
- * An evaluator for calculating osu!standard Aim skill.
1896
+ * Represents the skill required to read every object in the beatmap.
1430
1897
  */
1431
- declare abstract class OsuAimEvaluator extends AimEvaluator {
1432
- /**
1433
- * Evaluates the difficulty of aiming the current object, based on:
1434
- *
1435
- * - cursor velocity to the current object,
1436
- * - angle difficulty,
1437
- * - sharp velocity increases,
1438
- * - and slider difficulty.
1439
- *
1440
- * @param current The current object.
1441
- * @param withSliders Whether to take slider difficulty into account.
1442
- */
1443
- static evaluateDifficultyOf(current: DifficultyHitObject, withSliders: boolean): number;
1898
+ declare class DroidReading extends HarmonicSkill {
1899
+ private readonly clockRate;
1900
+ private readonly hitObjects;
1901
+ private currentDifficulty;
1902
+ private readonly skillMultiplier;
1903
+ private readonly difficultyDecayBase;
1904
+ constructor(mods: ModMap, clockRate: number, hitObjects: readonly PlaceableHitObject[]);
1905
+ countTopWeightedObjectDifficulties(difficultyValue: number): number;
1906
+ protected objectDifficultyOf(current: DroidDifficultyHitObject): number;
1907
+ protected applyDifficultyTransformation(difficulties: number[]): void;
1908
+ protected saveToHitObject(current: DroidDifficultyHitObject, difficulty: number): void;
1909
+ private calculateAdjustedDifficulty;
1910
+ private calculateReducedNoteCount;
1911
+ private difficultyDecay;
1444
1912
  }
1445
1913
 
1446
1914
  /**
1447
- * Represents the skill required to memorize and hit every object in a beatmap with the Flashlight mod enabled.
1915
+ * Represents the skill required to properly follow a beatmap's rhythm.
1448
1916
  */
1449
- declare class OsuFlashlight extends OsuSkill {
1450
- protected readonly skillMultiplier: number;
1451
- protected readonly strainDecayBase: number;
1452
- protected readonly reducedSectionCount: number;
1453
- protected readonly reducedSectionBaseline: number;
1454
- protected readonly decayWeight: number;
1455
- private readonly isHidden;
1456
- constructor(mods: Mod[]);
1457
- /**
1458
- * @param current The hitobject to calculate.
1459
- */
1460
- protected strainValueAt(current: DifficultyHitObject): number;
1461
- protected saveToHitObject(current: DifficultyHitObject): void;
1917
+ declare class DroidRhythm extends HarmonicSkill {
1918
+ protected readonly harmonicScale = 25;
1919
+ protected readonly decayExponent = 0.8;
1920
+ private readonly skillMultiplier;
1921
+ private readonly strainDecayBase;
1922
+ private currentRhythmDifficulty;
1923
+ private currentRhythmMultiplier;
1924
+ private readonly useSliderAccuracy;
1925
+ constructor(mods: ModMap);
1926
+ protected objectDifficultyOf(current: DroidDifficultyHitObject): number;
1927
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
1928
+ private strainDecay;
1462
1929
  }
1463
1930
 
1464
1931
  /**
1465
- * An evaluator for calculating osu!standard Flashlight skill.
1932
+ * Represents the skill required to press keys or tap with regards to keeping up with the speed at which objects need to be hit.
1466
1933
  */
1467
- declare abstract class OsuFlashlightEvaluator extends FlashlightEvaluator {
1934
+ declare class DroidTap extends HarmonicSkill {
1935
+ protected readonly harmonicScale = 20;
1936
+ private readonly skillMultiplier;
1937
+ private readonly strainDecayBase;
1938
+ private currentTapDifficulty;
1939
+ private currentRhythmMultiplier;
1940
+ private readonly sliderDifficulties;
1941
+ private maxDifficulty;
1942
+ readonly considerCheesability: boolean;
1943
+ constructor(mods: ModMap, considerCheesability: boolean);
1468
1944
  /**
1469
- * Evaluates the difficulty of memorizing and hitting the current object, based on:
1470
- *
1471
- * - distance between a number of previous objects and the current object,
1472
- * - the visual opacity of the current object,
1473
- * - the angle made by the current object,
1474
- * - length and speed of the current object (for sliders),
1475
- * - and whether Hidden mod is enabled.
1945
+ * The amount of notes that are relevant to the difficulty.
1946
+ */
1947
+ relevantNoteCount(): number;
1948
+ /**
1949
+ * Obtains the amount of sliders that are considered difficult in terms of relative difficulty, weighted by consistency.
1476
1950
  *
1477
- * @param current The current object.
1478
- * @param isHiddenMod Whether the Hidden mod is enabled.
1951
+ * @param difficultyValue The final difficulty value.
1479
1952
  */
1480
- static evaluateDifficultyOf(current: DifficultyHitObject, isHiddenMod: boolean): number;
1953
+ countTopWeightedSliders(difficultyValue: number): number;
1954
+ protected objectDifficultyOf(current: DroidDifficultyHitObject): number;
1955
+ protected saveToHitObject(current: DroidDifficultyHitObject): void;
1956
+ private strainDecay;
1481
1957
  }
1482
1958
 
1483
1959
  /**
1484
- * A performance points calculator that calculates performance points for osu!standard gamemode.
1960
+ * Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
1485
1961
  */
1486
- declare class OsuPerformanceCalculator extends PerformanceCalculator {
1487
- /**
1488
- * The aim performance value.
1489
- */
1490
- aim: number;
1491
- /**
1492
- * The speed performance value.
1493
- */
1494
- speed: number;
1495
- /**
1496
- * The accuracy performance value.
1497
- */
1498
- accuracy: number;
1499
- /**
1500
- * The flashlight performance value.
1501
- */
1502
- flashlight: number;
1503
- readonly difficultyAttributes: OsuDifficultyAttributes;
1504
- protected finalMultiplier: number;
1505
- protected readonly mode: Modes;
1962
+ declare class OsuAim extends VariableLengthStrainSkill {
1963
+ private currentStrain;
1964
+ private readonly skillMultiplierSnap;
1965
+ private readonly skillMultiplierAgility;
1966
+ private readonly skillMultiplierFlow;
1967
+ private readonly skillMultiplierTotal;
1968
+ private readonly combinedSnapNormExponent;
1506
1969
  /**
1507
- * @param difficultyAttributes The difficulty attributes to calculate.
1970
+ * The number of sections with the highest strains, which the peak strain reductions will apply to.
1971
+ * This is done in order to decrease their impact on the overall difficulty of the beatmap.
1508
1972
  */
1509
- constructor(difficultyAttributes: OsuDifficultyAttributes);
1510
- protected calculateValues(): void;
1511
- protected calculateTotalValue(): void;
1973
+ private readonly reducedSectionTime;
1512
1974
  /**
1513
- * Calculates the aim performance value of the beatmap.
1975
+ * The baseline multiplier applied to the section with the biggest strain.
1514
1976
  */
1515
- private calculateAimValue;
1977
+ private readonly reducedStrainBaseline;
1978
+ private readonly sliderStrains;
1979
+ private maxSliderStrain;
1980
+ readonly withSliders: boolean;
1981
+ constructor(mods: ModMap, withSliders: boolean);
1516
1982
  /**
1517
- * Calculates the speed performance value of the beatmap.
1983
+ * Obtains the amount of sliders that are considered difficult in terms of relative strain.
1518
1984
  */
1519
- private calculateSpeedValue;
1985
+ countDifficultSliders(): number;
1520
1986
  /**
1521
- * Calculates the accuracy performance value of the beatmap.
1987
+ * Obtains the amount of sliders that are considered difficult in terms of relative strain, weighted by consistency.
1988
+ *
1989
+ * @param difficultyValue The final difficulty value.
1522
1990
  */
1523
- private calculateAccuracyValue;
1991
+ countTopWeightedSliders(difficultyValue: number): number;
1992
+ protected strainValueAt(current: OsuDifficultyHitObject): number;
1993
+ protected calculateInitialStrain(time: number, current: OsuDifficultyHitObject): number;
1994
+ protected saveToHitObject(current: OsuDifficultyHitObject): void;
1995
+ private calculateAdjustedDifficulty;
1996
+ private calculateTotalValue;
1524
1997
  /**
1525
- * Calculates the flashlight performance value of the beatmap.
1998
+ * Converts the ratio of snap to flow into the probability of snapping or flowing.
1999
+ *
2000
+ * Constraints:
2001
+ * - `P(snap) + P(flow) = 1` (the object is always either snapped or flowed)
2002
+ * - `P(snap) = f(snap / flow)` and `P(flow) = f(flow/snap)` (i.e., snap and flow are symmetric and
2003
+ * reversible). This means `f(x) + f(1/x) = 1`
2004
+ * - `0 <= f(x) <= 1` (cannot have negative or greater than 100% probability of snapping or flowing)
2005
+ *
2006
+ * This logistic function is a solution, which fits nicely with the general idea of interpolation and
2007
+ * provides a tuneable constant.
2008
+ *
2009
+ * @param ratio The ratio.
2010
+ * @returns The probability.
1526
2011
  */
1527
- private calculateFlashlightValue;
1528
- toString(): string;
2012
+ private calculateSnapFlowProbability;
2013
+ difficultyValue(): number;
2014
+ private getReducedStrainPeaks;
2015
+ private strainDecay;
1529
2016
  }
1530
2017
 
1531
2018
  /**
1532
- * An evaluator for calculating osu!standard Rhythm skill.
2019
+ * Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
2020
+ * and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
1533
2021
  */
1534
- declare abstract class OsuRhythmEvaluator extends RhythmEvaluator {
2022
+ declare abstract class OsuSkill extends StrainSkill {
1535
2023
  /**
1536
- * Calculates a rhythm multiplier for the difficulty of the tap associated
1537
- * with historic data of the current object.
1538
- *
1539
- * @param current The current object.
1540
- * @param greatWindow The great hit window of the current object.
2024
+ * The weight by which each strain value decays.
1541
2025
  */
1542
- static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number): number;
2026
+ protected abstract readonly decayWeight: number;
2027
+ protected difficulty: number;
2028
+ difficultyValue(): number;
1543
2029
  }
1544
2030
 
1545
2031
  /**
1546
- * Represents the skill required to press keys or tap with regards to keeping up with the speed at which objects need to be hit.
2032
+ * Represents the skill required to memorize and hit every object in a beatmap with the Flashlight mod enabled.
1547
2033
  */
1548
- declare class OsuSpeed extends OsuSkill {
1549
- protected readonly skillMultiplier: number;
1550
- protected readonly strainDecayBase: number;
1551
- protected readonly reducedSectionCount: number;
1552
- protected readonly reducedSectionBaseline: number;
1553
- protected readonly difficultyMultiplier: number;
1554
- protected readonly decayWeight: number;
1555
- private currentSpeedStrain;
1556
- private currentRhythm;
1557
- private readonly greatWindow;
1558
- constructor(mods: Mod[], greatWindow: number);
1559
- /**
1560
- * @param current The hitobject to calculate.
1561
- */
1562
- protected strainValueAt(current: DifficultyHitObject): number;
1563
- /**
1564
- * @param current The hitobject to save to.
1565
- */
1566
- protected saveToHitObject(current: DifficultyHitObject): void;
2034
+ declare class OsuFlashlight extends OsuSkill {
2035
+ private readonly totalObjects;
2036
+ protected readonly reducedSectionCount = 0;
2037
+ protected readonly reducedSectionBaseline = 1;
2038
+ protected readonly decayWeight = 1;
2039
+ private currentFlashlightStrain;
2040
+ private readonly skillMultiplier;
2041
+ static difficultyToPerformance(difficulty: number): number;
2042
+ constructor(mods: ModMap, totalObjects: number);
2043
+ difficultyValue(): number;
2044
+ protected strainValueAt(current: OsuDifficultyHitObject): number;
2045
+ protected calculateInitialStrain(time: number, current: OsuDifficultyHitObject): number;
2046
+ protected saveToHitObject(current: OsuDifficultyHitObject): void;
2047
+ private calculateAdjustedDifficulty;
2048
+ private strainDecay;
1567
2049
  }
1568
2050
 
1569
2051
  /**
1570
- * An evaluator for calculating osu!standard speed skill.
2052
+ * Represents the skill required to read every object in the beatmap.
2053
+ */
2054
+ declare class OsuReading extends HarmonicSkill {
2055
+ private readonly clockRate;
2056
+ private readonly hitObjects;
2057
+ private currentDifficulty;
2058
+ private readonly skillMultiplier;
2059
+ private readonly difficultyDecayBase;
2060
+ constructor(mods: ModMap, clockRate: number, hitObjects: readonly PlaceableHitObject[]);
2061
+ countTopWeightedObjectDifficulties(difficultyValue: number): number;
2062
+ protected objectDifficultyOf(current: OsuDifficultyHitObject): number;
2063
+ protected applyDifficultyTransformation(difficulties: number[]): void;
2064
+ protected saveToHitObject(current: DifficultyHitObject, difficulty: number): void;
2065
+ private calculateAdjustedDifficulty;
2066
+ private calculateReducedNoteCount;
2067
+ private difficultyDecay;
2068
+ }
2069
+
2070
+ /**
2071
+ * Represents the skill required to press keys or tap with regards to keeping up with the speed at which objects need to be hit.
1571
2072
  */
1572
- declare abstract class OsuSpeedEvaluator extends SpeedEvaluator {
2073
+ declare class OsuSpeed extends HarmonicSkill {
2074
+ protected readonly harmonicScale = 20;
2075
+ private currentDifficulty;
2076
+ private currentRhythm;
2077
+ private readonly skillMultiplier;
2078
+ private readonly strainDecayBase;
2079
+ private readonly sliderDifficulties;
2080
+ private maxDifficulty;
1573
2081
  /**
1574
- * Spacing threshold for a single hitobject spacing.
2082
+ * The amount of notes that are relevant to the difficulty.
1575
2083
  */
1576
- private static readonly SINGLE_SPACING_THRESHOLD;
2084
+ relevantNoteCount(): number;
1577
2085
  /**
1578
- * Evaluates the difficulty of tapping the current object, based on:
2086
+ * Obtains the amount of sliders that are considered difficult in terms of relative difficulty, weighted by consistency.
1579
2087
  *
1580
- * - time between pressing the previous and current object,
1581
- * - distance between those objects,
1582
- * - and how easily they can be cheesed.
1583
- *
1584
- * @param current The current object.
1585
- * @param greatWindow The great hit window of the current object.
2088
+ * @param difficultyValue The final difficulty value.
1586
2089
  */
1587
- static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number): number;
2090
+ countTopWeightedSliders(difficultyValue: number): number;
2091
+ protected objectDifficultyOf(current: OsuDifficultyHitObject): number;
2092
+ protected saveToHitObject(current: OsuDifficultyHitObject): void;
2093
+ private calculateAdjustedDifficulty;
2094
+ private strainDecay;
1588
2095
  }
1589
2096
 
1590
- export { AimEvaluator, CalculationOptions, DifficultSlider, DifficultyAttributes, DifficultyCalculationOptions, DifficultyCalculator, DifficultyHitObject, DifficultyHitObjectCreator, DroidAim, DroidAimEvaluator, DroidDifficultyAttributes, DroidDifficultyCalculator, DroidFlashlight, DroidFlashlightEvaluator, DroidPerformanceCalculator, DroidRhythm, DroidRhythmEvaluator, DroidTap, DroidTapEvaluator, DroidVisual, DroidVisualEvaluator, ExtendedDroidDifficultyAttributes, FlashlightEvaluator, HighStrainSection, MapStars, OsuAim, OsuAimEvaluator, OsuDifficultyAttributes, OsuDifficultyCalculator, OsuFlashlight, OsuFlashlightEvaluator, OsuPerformanceCalculator, OsuRhythmEvaluator, OsuSpeed, OsuSpeedEvaluator, PerformanceCalculationOptions, PerformanceCalculator, RhythmEvaluator, SpeedEvaluator, StrainPeaks };
2097
+ export { DifficultyAttributes, DifficultyCalculator, DifficultyHitObject, DroidAgilityEvaluator, DroidAim, DroidDifficultyAttributes, DroidDifficultyCalculator, DroidDifficultyHitObject, DroidFlashlight, DroidFlashlightEvaluator, DroidFlowAimEvaluator, DroidPerformanceCalculator, DroidReading, DroidReadingEvaluator, DroidRhythm, DroidRhythmEvaluator, DroidSnapAimEvaluator, DroidTap, DroidTapEvaluator, ExtendedDroidDifficultyAttributes, OsuAgilityEvaluator, OsuAim, OsuDifficultyAttributes, OsuDifficultyCalculator, OsuDifficultyHitObject, OsuFlashlight, OsuFlashlightEvaluator, OsuFlowAimEvaluator, OsuPerformanceCalculator, OsuReading, OsuReadingEvaluator, OsuRhythmEvaluator, OsuSnapAimEvaluator, OsuSpeed, OsuSpeedEvaluator, PerformanceCalculator };
2098
+ export type { CacheableDifficultyAttributes, DifficultSlider, HighStrainSection, IDifficultyAttributes, IDroidDifficultyAttributes, IDroidPerformanceAttributes, IExtendedDroidDifficultyAttributes, IOsuDifficultyAttributes, IOsuPerformanceAttributes, IPerformanceAttributes, PerformanceCalculationOptions, StrainPeaks, TimedDifficultyAttributes, TimedStrainPeak };