@rian8337/osu-base 2.0.0-alpha.6 → 2.0.0
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/README.md +261 -18
- package/dist/index.js +233 -82
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/typings/index.d.ts +163 -39
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
2354
|
+
return this.processParsingOptions(finalMods, options);
|
|
2280
2355
|
}
|
|
2281
2356
|
/**
|
|
2282
|
-
* Checks for mods that are
|
|
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
|
-
|
|
2298
|
-
|
|
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
|
-
//
|
|
2506
|
+
// Apply EZ or HR to OD.
|
|
2407
2507
|
this.od = Math.min(this.od * statisticsMultiplier, 10);
|
|
2408
|
-
//
|
|
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
|
-
|
|
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 *
|
|
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
|
|
5352
|
+
return this;
|
|
5241
5353
|
}
|
|
5242
5354
|
/**
|
|
5243
5355
|
* Writes a line to encoded text.
|
|
@@ -5670,6 +5782,12 @@ class StoryboardVariablesEncoder extends StoryboardBaseEncoder {
|
|
|
5670
5782
|
}
|
|
5671
5783
|
}
|
|
5672
5784
|
|
|
5785
|
+
/**
|
|
5786
|
+
* A storyboard encoder.
|
|
5787
|
+
*
|
|
5788
|
+
* Note that this storyboard encoder does not encode storyboards, and as such equality with the
|
|
5789
|
+
* original beatmap or storyboard file is not guaranteed (and usually will not be equal).
|
|
5790
|
+
*/
|
|
5673
5791
|
class StoryboardEncoder extends Encoder {
|
|
5674
5792
|
finalResult = "";
|
|
5675
5793
|
encoders = [];
|
|
@@ -5709,7 +5827,7 @@ class BeatmapEventsEncoder extends BeatmapBaseEncoder {
|
|
|
5709
5827
|
this.writeLine(`2,${b.startTime},${b.endTime}`);
|
|
5710
5828
|
}
|
|
5711
5829
|
if (this.map.events.storyboard) {
|
|
5712
|
-
this.writeLine(new StoryboardEncoder(this.map.events.storyboard, false).encode());
|
|
5830
|
+
this.writeLine(new StoryboardEncoder(this.map.events.storyboard, false).encode().result);
|
|
5713
5831
|
}
|
|
5714
5832
|
else {
|
|
5715
5833
|
this.writeLine("//Storyboard Layer 0 (Background)");
|
|
@@ -6125,31 +6243,16 @@ class MapInfo {
|
|
|
6125
6243
|
*/
|
|
6126
6244
|
videoAvailable = false;
|
|
6127
6245
|
/**
|
|
6128
|
-
* The
|
|
6246
|
+
* The decoded beatmap from beatmap decoder.
|
|
6129
6247
|
*/
|
|
6130
|
-
get
|
|
6131
|
-
return
|
|
6248
|
+
get beatmap() {
|
|
6249
|
+
return this.cachedBeatmap;
|
|
6132
6250
|
}
|
|
6133
|
-
cachedBeatmap;
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
*/
|
|
6139
|
-
static async getInformation(params) {
|
|
6140
|
-
params.file ??= true;
|
|
6141
|
-
const beatmapID = params.beatmapID;
|
|
6142
|
-
const hash = params.hash;
|
|
6143
|
-
if (!beatmapID && !hash) {
|
|
6144
|
-
throw new Error("Beatmap ID or MD5 hash must be defined");
|
|
6145
|
-
}
|
|
6146
|
-
const apiRequestBuilder = new OsuAPIRequestBuilder().setEndpoint("get_beatmaps");
|
|
6147
|
-
if (beatmapID) {
|
|
6148
|
-
apiRequestBuilder.addParameter("b", beatmapID);
|
|
6149
|
-
}
|
|
6150
|
-
else if (hash) {
|
|
6151
|
-
apiRequestBuilder.addParameter("h", hash);
|
|
6152
|
-
}
|
|
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);
|
|
6153
6256
|
const map = new MapInfo();
|
|
6154
6257
|
const result = await apiRequestBuilder.sendRequest();
|
|
6155
6258
|
if (result.statusCode !== 200) {
|
|
@@ -6157,13 +6260,13 @@ class MapInfo {
|
|
|
6157
6260
|
}
|
|
6158
6261
|
const mapinfo = JSON.parse(result.data.toString("utf-8"))[0];
|
|
6159
6262
|
if (!mapinfo) {
|
|
6160
|
-
return
|
|
6263
|
+
return null;
|
|
6161
6264
|
}
|
|
6162
6265
|
if (parseInt(mapinfo.mode) !== 0) {
|
|
6163
|
-
return
|
|
6266
|
+
return null;
|
|
6164
6267
|
}
|
|
6165
6268
|
map.fillMetadata(mapinfo);
|
|
6166
|
-
if (
|
|
6269
|
+
if (downloadBeatmap !== false) {
|
|
6167
6270
|
await map.retrieveBeatmapFile();
|
|
6168
6271
|
}
|
|
6169
6272
|
return map;
|
|
@@ -6226,15 +6329,23 @@ class MapInfo {
|
|
|
6226
6329
|
this.videoAvailable = !!parseInt(mapinfo.video);
|
|
6227
6330
|
return this;
|
|
6228
6331
|
}
|
|
6332
|
+
/**
|
|
6333
|
+
* Checks whether the beatmap file has been downloaded.
|
|
6334
|
+
*/
|
|
6335
|
+
hasDownloadedBeatmap() {
|
|
6336
|
+
return this.cachedBeatmap !== null;
|
|
6337
|
+
}
|
|
6229
6338
|
/**
|
|
6230
6339
|
* Retrieves the .osu file of the beatmap.
|
|
6231
6340
|
*
|
|
6232
|
-
*
|
|
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.
|
|
6233
6344
|
*/
|
|
6234
|
-
retrieveBeatmapFile(
|
|
6345
|
+
retrieveBeatmapFile(force) {
|
|
6235
6346
|
return new Promise((resolve) => {
|
|
6236
|
-
if (this.
|
|
6237
|
-
return resolve(
|
|
6347
|
+
if (this.hasDownloadedBeatmap() && !force) {
|
|
6348
|
+
return resolve();
|
|
6238
6349
|
}
|
|
6239
6350
|
const url = `https://osu.ppy.sh/osu/${this.beatmapID}`;
|
|
6240
6351
|
const dataArray = [];
|
|
@@ -6244,10 +6355,10 @@ class MapInfo {
|
|
|
6244
6355
|
})
|
|
6245
6356
|
.on("complete", (response) => {
|
|
6246
6357
|
if (response.statusCode !== 200) {
|
|
6247
|
-
return resolve(
|
|
6358
|
+
return resolve();
|
|
6248
6359
|
}
|
|
6249
6360
|
this.cachedBeatmap = new BeatmapDecoder().decode(Buffer.concat(dataArray).toString("utf8")).result;
|
|
6250
|
-
resolve(
|
|
6361
|
+
resolve();
|
|
6251
6362
|
});
|
|
6252
6363
|
});
|
|
6253
6364
|
}
|
|
@@ -6307,10 +6418,12 @@ class MapInfo {
|
|
|
6307
6418
|
*
|
|
6308
6419
|
* - Option `0`: return map title and mods used if defined
|
|
6309
6420
|
* - Option `1`: return song source and map download link to beatmap mirrors
|
|
6310
|
-
* - Option `2`: return
|
|
6311
|
-
* - Option `3`: return
|
|
6312
|
-
* - Option `4`: return
|
|
6313
|
-
* - Option `5`: return
|
|
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
|
|
6314
6427
|
*
|
|
6315
6428
|
* @param option The option to pick.
|
|
6316
6429
|
* @param stats The custom statistics to apply. This will only be used to apply mods, custom speed multiplier, and force AR.
|
|
@@ -6333,13 +6446,9 @@ class MapInfo {
|
|
|
6333
6446
|
mapParams.speedMultiplier =
|
|
6334
6447
|
stats.speedMultiplier ?? mapParams.speedMultiplier;
|
|
6335
6448
|
}
|
|
6336
|
-
const mapStatistics = new MapStats(mapParams).calculate();
|
|
6337
|
-
mapStatistics.cs = parseFloat(mapStatistics.cs.toFixed(2));
|
|
6338
|
-
mapStatistics.ar = parseFloat(mapStatistics.ar.toFixed(2));
|
|
6339
|
-
mapStatistics.od = parseFloat(mapStatistics.od.toFixed(2));
|
|
6340
|
-
mapStatistics.hp = parseFloat(mapStatistics.hp.toFixed(2));
|
|
6341
6449
|
switch (option) {
|
|
6342
6450
|
case 0: {
|
|
6451
|
+
const mapStatistics = new MapStats(mapParams).calculate();
|
|
6343
6452
|
let string = `${this.fullTitle}${(mapStatistics.mods.length ?? 0) > 0
|
|
6344
6453
|
? ` +${mapStatistics.mods
|
|
6345
6454
|
.map((m) => m.acronym)
|
|
@@ -6383,19 +6492,65 @@ class MapInfo {
|
|
|
6383
6492
|
return string;
|
|
6384
6493
|
}
|
|
6385
6494
|
case 2:
|
|
6386
|
-
return `**Circles**: ${this.circles} - **Sliders**: ${this.sliders} - **Spinners**: ${this.spinners}
|
|
6495
|
+
return `**Circles**: ${this.circles} - **Sliders**: ${this.sliders} - **Spinners**: ${this.spinners}`;
|
|
6387
6496
|
case 3: {
|
|
6388
|
-
const
|
|
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();
|
|
6389
6546
|
const convertedBPM = this.convertBPM(mapStatistics);
|
|
6390
6547
|
let string = "**BPM**: ";
|
|
6391
|
-
if (this.
|
|
6392
|
-
const uninheritedTimingPoints = this.
|
|
6548
|
+
if (this.beatmap) {
|
|
6549
|
+
const uninheritedTimingPoints = this.beatmap.controlPoints.timing.points;
|
|
6393
6550
|
if (uninheritedTimingPoints.length === 1) {
|
|
6394
6551
|
string += `${this.bpm}${!Precision.almostEqualsNumber(this.bpm, convertedBPM)
|
|
6395
6552
|
? ` (${convertedBPM})`
|
|
6396
|
-
: ""} - **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x
|
|
6397
|
-
? `\n**Max Score**: ${maxScore.toLocaleString()}`
|
|
6398
|
-
: ""}`;
|
|
6553
|
+
: ""} - **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x`;
|
|
6399
6554
|
}
|
|
6400
6555
|
else {
|
|
6401
6556
|
let maxBPM = this.bpm;
|
|
@@ -6422,23 +6577,19 @@ class MapInfo {
|
|
|
6422
6577
|
string += `(${convertedBPM}) `;
|
|
6423
6578
|
}
|
|
6424
6579
|
}
|
|
6425
|
-
string += `- **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x
|
|
6426
|
-
? `\n**Max score**: ${maxScore.toLocaleString()}`
|
|
6427
|
-
: ""}`;
|
|
6580
|
+
string += `- **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x`;
|
|
6428
6581
|
}
|
|
6429
6582
|
}
|
|
6430
6583
|
else {
|
|
6431
6584
|
string += `${this.bpm}${!Precision.almostEqualsNumber(this.bpm, convertedBPM)
|
|
6432
6585
|
? ` (${convertedBPM})`
|
|
6433
|
-
: ""} - **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x
|
|
6434
|
-
? `\n**Max score**: ${maxScore.toLocaleString()}`
|
|
6435
|
-
: ""}`;
|
|
6586
|
+
: ""} - **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x`;
|
|
6436
6587
|
}
|
|
6437
6588
|
return string;
|
|
6438
6589
|
}
|
|
6439
|
-
case
|
|
6590
|
+
case 6:
|
|
6440
6591
|
return `**Last Update**: ${this.lastUpdate.toUTCString()} | **${this.convertStatus()}**`;
|
|
6441
|
-
case
|
|
6592
|
+
case 7:
|
|
6442
6593
|
return `❤️ **${this.favorites.toLocaleString()}** - ▶️ **${this.plays.toLocaleString()}**`;
|
|
6443
6594
|
default:
|
|
6444
6595
|
throw {
|