@rian8337/osu-difficulty-calculator 4.0.0-beta.10 → 4.0.0-beta.101

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