@rian8337/osu-base 2.0.0-alpha.7 → 2.0.1

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.
package/dist/index.js CHANGED
@@ -815,9 +815,7 @@ class Slider extends HitObject {
815
815
  startTime: finalSpanEndTime,
816
816
  });
817
817
  this.nestedHitObjects.push(this.tail);
818
- this.nestedHitObjects.sort((a, b) => {
819
- return a.startTime - b.startTime;
820
- });
818
+ this.nestedHitObjects.sort((a, b) => a.startTime - b.startTime);
821
819
  }
822
820
  toString() {
823
821
  return `Position: [${this.position.x}, ${this.position.y}], distance: ${this.path.expectedDistance}, repetitions: ${this.repetitions}, slider ticks: ${this.nestedHitObjects.filter((v) => v instanceof SliderTick).length}`;
@@ -1196,6 +1194,8 @@ class ControlPointManager {
1196
1194
  /**
1197
1195
  * Removes a control point.
1198
1196
  *
1197
+ * This method will remove the earliest control point in the array that is equal to the given control point.
1198
+ *
1199
1199
  * @param controlPoint The control point to remove.
1200
1200
  * @returns Whether the control point was removed.
1201
1201
  */
@@ -1872,6 +1872,51 @@ class HitWindow {
1872
1872
  * Represents the hit window of osu!droid.
1873
1873
  */
1874
1874
  class DroidHitWindow extends HitWindow {
1875
+ /**
1876
+ * Calculates the overall difficulty value of a great hit window.
1877
+ *
1878
+ * @param value The value of the hit window, in milliseconds.
1879
+ * @param isPrecise Whether to calculate for Precise mod.
1880
+ * @returns The overall difficulty value.
1881
+ */
1882
+ static hitWindow300ToOD(value, isPrecise) {
1883
+ if (isPrecise) {
1884
+ return 5 - (value - 55) / 6;
1885
+ }
1886
+ else {
1887
+ return 5 - (value - 75) / 5;
1888
+ }
1889
+ }
1890
+ /**
1891
+ * Calculates the overall difficulty value of a good hit window.
1892
+ *
1893
+ * @param value The value of the hit window, in milliseconds.
1894
+ * @param isPrecise Whether to calculate for Precise mod.
1895
+ * @returns The overall difficulty value.
1896
+ */
1897
+ static hitWindow100ToOD(value, isPrecise) {
1898
+ if (isPrecise) {
1899
+ return 5 - (value - 120) / 8;
1900
+ }
1901
+ else {
1902
+ return 5 - (value - 150) / 10;
1903
+ }
1904
+ }
1905
+ /**
1906
+ * Calculates the overall difficulty value of a meh hit window.
1907
+ *
1908
+ * @param value The value of the hit window, in milliseconds.
1909
+ * @param isPrecise Whether to calculate for Precise mod.
1910
+ * @returns The overall difficulty value.
1911
+ */
1912
+ static hitWindow50ToOD(value, isPrecise) {
1913
+ if (isPrecise) {
1914
+ return 5 - (value - 180) / 10;
1915
+ }
1916
+ else {
1917
+ return 5 - (value - 250) / 10;
1918
+ }
1919
+ }
1875
1920
  hitWindowFor300(isPrecise) {
1876
1921
  if (isPrecise) {
1877
1922
  return 55 + 6 * (5 - this.overallDifficulty);
@@ -1901,6 +1946,33 @@ class DroidHitWindow extends HitWindow {
1901
1946
  * Represents the hit window of osu!standard.
1902
1947
  */
1903
1948
  class OsuHitWindow extends HitWindow {
1949
+ /**
1950
+ * Calculates the overall difficulty value of a great hit window.
1951
+ *
1952
+ * @param value The value of the hit window, in milliseconds.
1953
+ * @returns The overall difficulty value.
1954
+ */
1955
+ static hitWindow300ToOD(value) {
1956
+ return (80 - value) / 6;
1957
+ }
1958
+ /**
1959
+ * Calculates the overall difficulty value of a good hit window.
1960
+ *
1961
+ * @param value The value of the hit window, in milliseconds.
1962
+ * @returns The overall difficulty value.
1963
+ */
1964
+ static hitWindow100ToOD(value) {
1965
+ return (140 - value) / 8;
1966
+ }
1967
+ /**
1968
+ * Calculates the overall difficulty value of a meh hit window.
1969
+ *
1970
+ * @param value The value of the hit window, in milliseconds.
1971
+ * @returns The overall difficulty value.
1972
+ */
1973
+ static hitWindow50ToOD(value) {
1974
+ return (200 - value) / 10;
1975
+ }
1904
1976
  hitWindowFor300() {
1905
1977
  return 80 - 6 * this.overallDifficulty;
1906
1978
  }
@@ -2076,6 +2148,8 @@ class ModFlashlight extends Mod {
2076
2148
  * Represents the Hidden mod.
2077
2149
  */
2078
2150
  class ModHidden extends Mod {
2151
+ static fadeInDurationMultiplier = 0.4;
2152
+ static fadeOutDurationMultiplier = 0.3;
2079
2153
  scoreMultiplier = 1.06;
2080
2154
  acronym = "HD";
2081
2155
  name = "Hidden";
@@ -2234,9 +2308,7 @@ class ModUtil {
2234
2308
  * Mods that change the way the map looks.
2235
2309
  */
2236
2310
  static mapChangingMods = [
2237
- new ModDoubleTime(),
2238
- new ModNightCore(),
2239
- new ModHalfTime(),
2311
+ ...this.speedChangingMods,
2240
2312
  new ModEasy(),
2241
2313
  new ModHardRock(),
2242
2314
  new ModSmallCircle(),
@@ -2245,24 +2317,27 @@ class ModUtil {
2245
2317
  * Gets a list of mods from a droid mod string, such as "hd".
2246
2318
  *
2247
2319
  * @param str The string.
2320
+ * @param options Options for parsing behavior.
2248
2321
  */
2249
- static droidStringToMods(str) {
2250
- return this.checkDuplicateMods(this.allMods.filter((m) => m.droidString && str.toLowerCase().includes(m.droidString)));
2322
+ static droidStringToMods(str, options) {
2323
+ return this.processParsingOptions(this.allMods.filter((m) => m.droidString && str.toLowerCase().includes(m.droidString)), options);
2251
2324
  }
2252
2325
  /**
2253
2326
  * Gets a list of mods from a PC modbits.
2254
2327
  *
2255
2328
  * @param modbits The modbits.
2329
+ * @param options Options for parsing behavior.
2256
2330
  */
2257
- static pcModbitsToMods(modbits) {
2258
- return this.checkDuplicateMods(this.allMods.filter((m) => m.bitwise & modbits));
2331
+ static pcModbitsToMods(modbits, options) {
2332
+ return this.processParsingOptions(this.allMods.filter((m) => m.bitwise & modbits), options);
2259
2333
  }
2260
2334
  /**
2261
2335
  * Gets a list of mods from a PC mod string, such as "HDHR".
2262
2336
  *
2263
2337
  * @param str The string.
2338
+ * @param options Options for parsing behavior.
2264
2339
  */
2265
- static pcStringToMods(str) {
2340
+ static pcStringToMods(str, options) {
2266
2341
  const finalMods = [];
2267
2342
  str = str.toLowerCase();
2268
2343
  while (str) {
@@ -2276,14 +2351,25 @@ class ModUtil {
2276
2351
  }
2277
2352
  str = str.slice(nchars);
2278
2353
  }
2279
- return this.checkDuplicateMods(finalMods);
2354
+ return this.processParsingOptions(finalMods, options);
2280
2355
  }
2281
2356
  /**
2282
- * Checks for mods that are duplicate and incompatible with each other.
2357
+ * Checks for mods that are duplicated.
2283
2358
  *
2284
2359
  * @param mods The mods to check for.
2360
+ * @returns Mods that have been filtered.
2285
2361
  */
2286
2362
  static checkDuplicateMods(mods) {
2363
+ return Array.from(new Set(mods));
2364
+ }
2365
+ /**
2366
+ * Checks for mods that are incompatible with each other.
2367
+ *
2368
+ * This will modify the original array.
2369
+ *
2370
+ * @param mods The mods to check for.
2371
+ */
2372
+ static checkIncompatibleMods(mods) {
2287
2373
  for (const incompatibleMod of this.incompatibleMods) {
2288
2374
  const fulfilledMods = mods.filter((m) => incompatibleMod.map((v) => v.acronym).includes(m.acronym));
2289
2375
  if (fulfilledMods.length > 1) {
@@ -2294,8 +2380,22 @@ class ModUtil {
2294
2380
  mods.push(fulfilledMods[0]);
2295
2381
  }
2296
2382
  }
2297
- // Check for duplicate mod entries
2298
- return Array.from(new Set(mods));
2383
+ }
2384
+ /**
2385
+ * Processes parsing options.
2386
+ *
2387
+ * @param mods The mods to process.
2388
+ * @param options The options to process.
2389
+ * @returns The processed mods.
2390
+ */
2391
+ static processParsingOptions(mods, options) {
2392
+ if (options?.checkDuplicate !== false) {
2393
+ mods = this.checkDuplicateMods(mods);
2394
+ }
2395
+ if (options?.checkIncompatible !== false) {
2396
+ this.checkIncompatibleMods(mods);
2397
+ }
2398
+ return mods;
2299
2399
  }
2300
2400
  }
2301
2401
 
@@ -2394,29 +2494,31 @@ class MapStats {
2394
2494
  }
2395
2495
  switch (params?.mode ?? exports.modes.osu) {
2396
2496
  case exports.modes.droid:
2397
- // In droid pre-1.6.8, NC speed multiplier is assumed bugged (1.39)
2497
+ // In droid pre-1.6.8, NC speed multiplier is assumed bugged (1.39).
2398
2498
  if (this.mods.some((m) => m instanceof ModNightCore) &&
2399
2499
  this.oldStatistics) {
2400
2500
  this.speedMultiplier *= 1.39 / 1.5;
2401
2501
  }
2402
2502
  // CS and OD work differently in droid, therefore it
2403
2503
  // needs to be computed regardless of map-changing mods
2404
- // and statistics multiplier
2504
+ // and statistics multiplier.
2405
2505
  if (this.od !== undefined) {
2406
- // apply EZ or HR to OD
2506
+ // Apply EZ or HR to OD.
2407
2507
  this.od = Math.min(this.od * statisticsMultiplier, 10);
2408
- // convert original OD to droid OD
2508
+ // Convert original OD to droid hit window to take
2509
+ // droid hit window and the PR mod in mind.
2409
2510
  const droidToMS = new DroidHitWindow(this.od).hitWindowFor300(this.mods.some((m) => m instanceof ModPrecise)) / this.speedMultiplier;
2410
- this.od = 5 - (droidToMS - 50) / 6;
2511
+ // Convert droid hit window back to original OD.
2512
+ this.od = OsuHitWindow.hitWindow300ToOD(droidToMS);
2411
2513
  }
2412
2514
  // HR and EZ works differently in droid in terms of
2413
- // CS modification (even CS in itself as well)
2515
+ // CS modification (even CS in itself as well).
2414
2516
  //
2415
2517
  // If present mods are found, they need to be removed
2416
2518
  // from the bitwise enum of mods to prevent double
2417
- // calculation
2519
+ // calculation.
2418
2520
  if (this.cs !== undefined) {
2419
- // Assume 681 is height
2521
+ // Assume 681 is height.
2420
2522
  const assumedHeight = 681;
2421
2523
  let scale = ((assumedHeight / 480) * (54.42 - this.cs * 4.48) * 2) /
2422
2524
  128 +
@@ -2433,7 +2535,8 @@ class MapStats {
2433
2535
  if (this.mods.some((m) => m instanceof ModSmallCircle)) {
2434
2536
  scale -= ((assumedHeight / 480) * (4 * 4.48) * 2) / 128;
2435
2537
  }
2436
- const radius = (64 * scale) / ((assumedHeight * 0.85) / 384);
2538
+ const radius = (64 * Math.max(1e-3, scale)) /
2539
+ ((assumedHeight * 0.85) / 384);
2437
2540
  this.cs = Math.min(5 + ((1 - radius / 32) * 5) / 0.7, 10);
2438
2541
  }
2439
2542
  if (this.hp !== undefined) {
@@ -5226,6 +5329,15 @@ class Encoder {
5226
5329
  * The result of the encoding process.
5227
5330
  */
5228
5331
  finalResult = "";
5332
+ /**
5333
+ * The result of the encoding process.
5334
+ */
5335
+ get result() {
5336
+ return this.finalResult;
5337
+ }
5338
+ /**
5339
+ * @param target The target of the encoding process.
5340
+ */
5229
5341
  constructor(target) {
5230
5342
  this.target = target;
5231
5343
  }
@@ -5237,7 +5349,7 @@ class Encoder {
5237
5349
  encode() {
5238
5350
  this.reset();
5239
5351
  this.encodeInternal();
5240
- return this.finalResult;
5352
+ return this;
5241
5353
  }
5242
5354
  /**
5243
5355
  * Writes a line to encoded text.
@@ -5715,7 +5827,7 @@ class BeatmapEventsEncoder extends BeatmapBaseEncoder {
5715
5827
  this.writeLine(`2,${b.startTime},${b.endTime}`);
5716
5828
  }
5717
5829
  if (this.map.events.storyboard) {
5718
- this.writeLine(new StoryboardEncoder(this.map.events.storyboard, false).encode());
5830
+ this.writeLine(new StoryboardEncoder(this.map.events.storyboard, false).encode().result);
5719
5831
  }
5720
5832
  else {
5721
5833
  this.writeLine("//Storyboard Layer 0 (Background)");
@@ -6131,31 +6243,16 @@ class MapInfo {
6131
6243
  */
6132
6244
  videoAvailable = false;
6133
6245
  /**
6134
- * The parsed beatmap from beatmap parser.
6246
+ * The decoded beatmap from beatmap decoder.
6135
6247
  */
6136
- get map() {
6137
- return Utils.deepCopy(this.cachedBeatmap);
6248
+ get beatmap() {
6249
+ return this.cachedBeatmap;
6138
6250
  }
6139
- cachedBeatmap;
6140
- /**
6141
- * Retrieve a beatmap's general information.
6142
- *
6143
- * Either beatmap ID or MD5 hash of the beatmap must be specified. If both are specified, beatmap ID is taken.
6144
- */
6145
- static async getInformation(params) {
6146
- params.file ??= true;
6147
- const beatmapID = params.beatmapID;
6148
- const hash = params.hash;
6149
- if (!beatmapID && !hash) {
6150
- throw new Error("Beatmap ID or MD5 hash must be defined");
6151
- }
6152
- const apiRequestBuilder = new OsuAPIRequestBuilder().setEndpoint("get_beatmaps");
6153
- if (beatmapID) {
6154
- apiRequestBuilder.addParameter("b", beatmapID);
6155
- }
6156
- else if (hash) {
6157
- apiRequestBuilder.addParameter("h", hash);
6158
- }
6251
+ cachedBeatmap = null;
6252
+ static async getInformation(beatmapIdOrHash, downloadBeatmap) {
6253
+ const apiRequestBuilder = new OsuAPIRequestBuilder()
6254
+ .setEndpoint("get_beatmaps")
6255
+ .addParameter(typeof beatmapIdOrHash === "string" ? "h" : "b", beatmapIdOrHash);
6159
6256
  const map = new MapInfo();
6160
6257
  const result = await apiRequestBuilder.sendRequest();
6161
6258
  if (result.statusCode !== 200) {
@@ -6163,13 +6260,13 @@ class MapInfo {
6163
6260
  }
6164
6261
  const mapinfo = JSON.parse(result.data.toString("utf-8"))[0];
6165
6262
  if (!mapinfo) {
6166
- return map;
6263
+ return null;
6167
6264
  }
6168
6265
  if (parseInt(mapinfo.mode) !== 0) {
6169
- return map;
6266
+ return null;
6170
6267
  }
6171
6268
  map.fillMetadata(mapinfo);
6172
- if (params.file) {
6269
+ if (downloadBeatmap !== false) {
6173
6270
  await map.retrieveBeatmapFile();
6174
6271
  }
6175
6272
  return map;
@@ -6232,15 +6329,23 @@ class MapInfo {
6232
6329
  this.videoAvailable = !!parseInt(mapinfo.video);
6233
6330
  return this;
6234
6331
  }
6332
+ /**
6333
+ * Checks whether the beatmap file has been downloaded.
6334
+ */
6335
+ hasDownloadedBeatmap() {
6336
+ return this.cachedBeatmap !== null;
6337
+ }
6235
6338
  /**
6236
6339
  * Retrieves the .osu file of the beatmap.
6237
6340
  *
6238
- * @param forceDownload Whether or not to download the file regardless if it's already available.
6341
+ * After this, you can use the `hasDownloadedBeatmap` method to check if the beatmap has been downloaded.
6342
+ *
6343
+ * @param force Whether to download the file regardless if it's already available.
6239
6344
  */
6240
- retrieveBeatmapFile(forceDownload) {
6345
+ retrieveBeatmapFile(force) {
6241
6346
  return new Promise((resolve) => {
6242
- if (this.cachedBeatmap && !forceDownload) {
6243
- return resolve(this);
6347
+ if (this.hasDownloadedBeatmap() && !force) {
6348
+ return resolve();
6244
6349
  }
6245
6350
  const url = `https://osu.ppy.sh/osu/${this.beatmapID}`;
6246
6351
  const dataArray = [];
@@ -6250,10 +6355,10 @@ class MapInfo {
6250
6355
  })
6251
6356
  .on("complete", (response) => {
6252
6357
  if (response.statusCode !== 200) {
6253
- return resolve(this);
6358
+ return resolve();
6254
6359
  }
6255
6360
  this.cachedBeatmap = new BeatmapDecoder().decode(Buffer.concat(dataArray).toString("utf8")).result;
6256
- resolve(this);
6361
+ resolve();
6257
6362
  });
6258
6363
  });
6259
6364
  }
@@ -6313,10 +6418,12 @@ class MapInfo {
6313
6418
  *
6314
6419
  * - Option `0`: return map title and mods used if defined
6315
6420
  * - Option `1`: return song source and map download link to beatmap mirrors
6316
- * - Option `2`: return CS, AR, OD, HP
6317
- * - Option `3`: return BPM, map length, max combo
6318
- * - Option `4`: return last update date and map status
6319
- * - Option `5`: return favorite count and play count
6421
+ * - Option `2`: return circle, slider, and spinner count
6422
+ * - Option `3`: return CS, AR, OD, HP, and max score statistics for droid
6423
+ * - Option `4`: return CS, AR, OD, HP, and max score statistics for PC
6424
+ * - Option `5`: return BPM, map length, and max combo
6425
+ * - Option `6`: return last update date and map status
6426
+ * - Option `7`: return favorite count and play count
6320
6427
  *
6321
6428
  * @param option The option to pick.
6322
6429
  * @param stats The custom statistics to apply. This will only be used to apply mods, custom speed multiplier, and force AR.
@@ -6339,13 +6446,9 @@ class MapInfo {
6339
6446
  mapParams.speedMultiplier =
6340
6447
  stats.speedMultiplier ?? mapParams.speedMultiplier;
6341
6448
  }
6342
- const mapStatistics = new MapStats(mapParams).calculate();
6343
- mapStatistics.cs = parseFloat(mapStatistics.cs.toFixed(2));
6344
- mapStatistics.ar = parseFloat(mapStatistics.ar.toFixed(2));
6345
- mapStatistics.od = parseFloat(mapStatistics.od.toFixed(2));
6346
- mapStatistics.hp = parseFloat(mapStatistics.hp.toFixed(2));
6347
6449
  switch (option) {
6348
6450
  case 0: {
6451
+ const mapStatistics = new MapStats(mapParams).calculate();
6349
6452
  let string = `${this.fullTitle}${(mapStatistics.mods.length ?? 0) > 0
6350
6453
  ? ` +${mapStatistics.mods
6351
6454
  .map((m) => m.acronym)
@@ -6389,19 +6492,65 @@ class MapInfo {
6389
6492
  return string;
6390
6493
  }
6391
6494
  case 2:
6392
- return `**Circles**: ${this.circles} - **Sliders**: ${this.sliders} - **Spinners**: ${this.spinners}\n**CS**: ${this.cs}${this.cs === mapStatistics.cs ? "" : ` (${mapStatistics.cs})`} - **AR**: ${this.ar}${this.ar === mapStatistics.ar ? "" : ` (${mapStatistics.ar})`} - **OD**: ${this.od}${this.od === mapStatistics.od ? "" : ` (${mapStatistics.od})`} - **HP**: ${this.hp}${this.hp === mapStatistics.hp ? "" : ` (${mapStatistics.hp})`}`;
6495
+ return `**Circles**: ${this.circles} - **Sliders**: ${this.sliders} - **Spinners**: ${this.spinners}`;
6393
6496
  case 3: {
6394
- const maxScore = this.map?.maxDroidScore(mapStatistics) ?? 0;
6497
+ const droidOriginalStats = new MapStats({
6498
+ cs: this.cs,
6499
+ ar: this.ar,
6500
+ od: this.od,
6501
+ hp: this.hp,
6502
+ }).calculate({ mode: exports.modes.droid });
6503
+ const droidModifiedStats = new MapStats(mapParams).calculate({ mode: exports.modes.droid });
6504
+ droidOriginalStats.cs = MathUtils.round(droidOriginalStats.cs, 2);
6505
+ droidOriginalStats.ar = MathUtils.round(droidOriginalStats.ar, 2);
6506
+ droidOriginalStats.od = MathUtils.round(droidOriginalStats.od, 2);
6507
+ droidOriginalStats.hp = MathUtils.round(droidOriginalStats.hp, 2);
6508
+ droidModifiedStats.cs = MathUtils.round(droidModifiedStats.cs, 2);
6509
+ droidModifiedStats.ar = MathUtils.round(droidModifiedStats.ar, 2);
6510
+ droidModifiedStats.od = MathUtils.round(droidModifiedStats.od, 2);
6511
+ droidModifiedStats.hp = MathUtils.round(droidModifiedStats.hp, 2);
6512
+ const maxScore = this.beatmap?.maxDroidScore(new MapStats(mapParams)) ?? 0;
6513
+ return `**CS**: ${droidOriginalStats.cs}${Precision.almostEqualsNumber(droidOriginalStats.cs, droidModifiedStats.cs)
6514
+ ? ""
6515
+ : ` (${droidModifiedStats.cs})`} - **AR**: ${droidOriginalStats.ar}${Precision.almostEqualsNumber(droidOriginalStats.ar, droidModifiedStats.ar)
6516
+ ? ""
6517
+ : ` (${droidModifiedStats.ar})`} - **OD**: ${droidOriginalStats.od}${Precision.almostEqualsNumber(droidOriginalStats.od, droidModifiedStats.od)
6518
+ ? ""
6519
+ : ` (${droidModifiedStats.od})`} - **HP**: ${droidOriginalStats.hp}${Precision.almostEqualsNumber(droidOriginalStats.hp, droidModifiedStats.hp)
6520
+ ? ""
6521
+ : ` (${droidModifiedStats.hp})`}${maxScore > 0
6522
+ ? `\n**Max Score**: ${maxScore.toLocaleString()}`
6523
+ : ""}`;
6524
+ }
6525
+ case 4: {
6526
+ const mapStatistics = new MapStats(mapParams).calculate();
6527
+ mapStatistics.cs = MathUtils.round(mapStatistics.cs, 2);
6528
+ mapStatistics.ar = MathUtils.round(mapStatistics.ar, 2);
6529
+ mapStatistics.od = MathUtils.round(mapStatistics.od, 2);
6530
+ mapStatistics.hp = MathUtils.round(mapStatistics.hp, 2);
6531
+ const maxScore = this.beatmap?.maxOsuScore(mapStatistics.mods) ?? 0;
6532
+ return `**CS**: ${this.cs}${Precision.almostEqualsNumber(this.cs, mapStatistics.cs)
6533
+ ? ""
6534
+ : ` (${mapStatistics.cs})`} - **AR**: ${this.ar}${Precision.almostEqualsNumber(this.ar, mapStatistics.ar)
6535
+ ? ""
6536
+ : ` (${mapStatistics.ar})`} - **OD**: ${this.od}${Precision.almostEqualsNumber(this.od, mapStatistics.od)
6537
+ ? ""
6538
+ : ` (${mapStatistics.od})`} - **HP**: ${this.hp}${Precision.almostEqualsNumber(this.hp, mapStatistics.hp)
6539
+ ? ""
6540
+ : ` (${mapStatistics.hp})`}${maxScore > 0
6541
+ ? `\n**Max Score**: ${maxScore.toLocaleString()}`
6542
+ : ""}`;
6543
+ }
6544
+ case 5: {
6545
+ const mapStatistics = new MapStats(mapParams).calculate();
6395
6546
  const convertedBPM = this.convertBPM(mapStatistics);
6396
6547
  let string = "**BPM**: ";
6397
- if (this.map) {
6398
- const uninheritedTimingPoints = this.map.controlPoints.timing.points;
6548
+ if (this.beatmap) {
6549
+ const uninheritedTimingPoints = this.beatmap.controlPoints.timing.points;
6399
6550
  if (uninheritedTimingPoints.length === 1) {
6400
6551
  string += `${this.bpm}${!Precision.almostEqualsNumber(this.bpm, convertedBPM)
6401
6552
  ? ` (${convertedBPM})`
6402
- : ""} - **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x${maxScore > 0
6403
- ? `\n**Max Score**: ${maxScore.toLocaleString()}`
6404
- : ""}`;
6553
+ : ""} - **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x`;
6405
6554
  }
6406
6555
  else {
6407
6556
  let maxBPM = this.bpm;
@@ -6428,23 +6577,19 @@ class MapInfo {
6428
6577
  string += `(${convertedBPM}) `;
6429
6578
  }
6430
6579
  }
6431
- string += `- **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x${maxScore > 0
6432
- ? `\n**Max score**: ${maxScore.toLocaleString()}`
6433
- : ""}`;
6580
+ string += `- **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x`;
6434
6581
  }
6435
6582
  }
6436
6583
  else {
6437
6584
  string += `${this.bpm}${!Precision.almostEqualsNumber(this.bpm, convertedBPM)
6438
6585
  ? ` (${convertedBPM})`
6439
- : ""} - **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x${maxScore > 0
6440
- ? `\n**Max score**: ${maxScore.toLocaleString()}`
6441
- : ""}`;
6586
+ : ""} - **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x`;
6442
6587
  }
6443
6588
  return string;
6444
6589
  }
6445
- case 4:
6590
+ case 6:
6446
6591
  return `**Last Update**: ${this.lastUpdate.toUTCString()} | **${this.convertStatus()}**`;
6447
- case 5:
6592
+ case 7:
6448
6593
  return `❤️ **${this.favorites.toLocaleString()}** - ▶️ **${this.plays.toLocaleString()}**`;
6449
6594
  default:
6450
6595
  throw {
@@ -6512,9 +6657,11 @@ exports.CommandTimelineGroup = CommandTimelineGroup;
6512
6657
  exports.CommandTrigger = CommandTrigger;
6513
6658
  exports.ControlPointManager = ControlPointManager;
6514
6659
  exports.DifficultyControlPoint = DifficultyControlPoint;
6660
+ exports.DifficultyControlPointManager = DifficultyControlPointManager;
6515
6661
  exports.DroidAPIRequestBuilder = DroidAPIRequestBuilder;
6516
6662
  exports.DroidHitWindow = DroidHitWindow;
6517
6663
  exports.EffectControlPoint = EffectControlPoint;
6664
+ exports.EffectControlPointManager = EffectControlPointManager;
6518
6665
  exports.HitObject = HitObject;
6519
6666
  exports.HitSampleInfo = HitSampleInfo;
6520
6667
  exports.Interpolation = Interpolation;
@@ -6549,6 +6696,7 @@ exports.Precision = Precision;
6549
6696
  exports.RGBColor = RGBColor;
6550
6697
  exports.SampleBankInfo = SampleBankInfo;
6551
6698
  exports.SampleControlPoint = SampleControlPoint;
6699
+ exports.SampleControlPointManager = SampleControlPointManager;
6552
6700
  exports.Slider = Slider;
6553
6701
  exports.SliderHead = SliderHead;
6554
6702
  exports.SliderPath = SliderPath;
@@ -6565,6 +6713,7 @@ exports.StoryboardLayer = StoryboardLayer;
6565
6713
  exports.StoryboardSample = StoryboardSample;
6566
6714
  exports.StoryboardSprite = StoryboardSprite;
6567
6715
  exports.TimingControlPoint = TimingControlPoint;
6716
+ exports.TimingControlPointManager = TimingControlPointManager;
6568
6717
  exports.Utils = Utils;
6569
6718
  exports.Vector2 = Vector2;
6570
6719
  //# sourceMappingURL=index.js.map