@rian8337/osu-difficulty-calculator 4.0.0-beta.6 → 4.0.0-beta.60

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