@rian8337/osu-base 2.2.0 → 3.0.0-beta.2
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 +215 -169
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/typings/index.d.ts +143 -161
package/dist/index.js
CHANGED
|
@@ -311,7 +311,9 @@ class APIRequestBuilder {
|
|
|
311
311
|
})
|
|
312
312
|
.on("complete", async (response) => {
|
|
313
313
|
++this.fetchAttempts;
|
|
314
|
-
|
|
314
|
+
const { statusCode } = response;
|
|
315
|
+
if ((statusCode === 500 || statusCode === 503) &&
|
|
316
|
+
this.fetchAttempts < 5) {
|
|
315
317
|
console.error(`Request to ${url} failed; ${this.fetchAttempts} attempts so far; retrying`);
|
|
316
318
|
await Utils.sleep(0.2);
|
|
317
319
|
return resolve(this.sendRequest());
|
|
@@ -342,9 +344,7 @@ class APIRequestBuilder {
|
|
|
342
344
|
* @param param The parameter to remove.
|
|
343
345
|
*/
|
|
344
346
|
removeParameter(param) {
|
|
345
|
-
|
|
346
|
-
this.params.delete(param);
|
|
347
|
-
}
|
|
347
|
+
this.params.delete(param);
|
|
348
348
|
return this;
|
|
349
349
|
}
|
|
350
350
|
}
|
|
@@ -355,18 +355,6 @@ class DroidAPIRequestBuilder extends APIRequestBuilder {
|
|
|
355
355
|
host = "https://osudroid.moe/api/";
|
|
356
356
|
APIkey = process.env.DROID_API_KEY;
|
|
357
357
|
APIkeyParam = `apiKey=${this.APIkey}&`;
|
|
358
|
-
setEndpoint(endpoint) {
|
|
359
|
-
// Compatibility with old API. Can be removed in v3.0.
|
|
360
|
-
switch (endpoint) {
|
|
361
|
-
case "banscore.php":
|
|
362
|
-
endpoint = "single_score_wipe.php";
|
|
363
|
-
break;
|
|
364
|
-
case "rename.php":
|
|
365
|
-
endpoint = "user_rename.php";
|
|
366
|
-
break;
|
|
367
|
-
}
|
|
368
|
-
return super.setEndpoint(endpoint);
|
|
369
|
-
}
|
|
370
358
|
}
|
|
371
359
|
/**
|
|
372
360
|
* API request builder for osu!standard.
|
|
@@ -377,6 +365,15 @@ class OsuAPIRequestBuilder extends APIRequestBuilder {
|
|
|
377
365
|
APIkeyParam = `k=${this.APIkey}&`;
|
|
378
366
|
}
|
|
379
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Mode enum to switch things between osu!droid and osu!standard.
|
|
370
|
+
*/
|
|
371
|
+
exports.modes = void 0;
|
|
372
|
+
(function (modes) {
|
|
373
|
+
modes["droid"] = "droid";
|
|
374
|
+
modes["osu"] = "osu";
|
|
375
|
+
})(exports.modes || (exports.modes = {}));
|
|
376
|
+
|
|
380
377
|
/**
|
|
381
378
|
* Bitmask constant of object types. This is needed as osu! uses bits to determine object types.
|
|
382
379
|
*/
|
|
@@ -390,15 +387,15 @@ exports.objectTypes = void 0;
|
|
|
390
387
|
})(exports.objectTypes || (exports.objectTypes = {}));
|
|
391
388
|
|
|
392
389
|
/**
|
|
393
|
-
*
|
|
390
|
+
* Represents a two-dimensional vector.
|
|
394
391
|
*/
|
|
395
392
|
class Vector2 {
|
|
396
393
|
/**
|
|
397
|
-
* The x position of
|
|
394
|
+
* The x position of this vector.
|
|
398
395
|
*/
|
|
399
396
|
x;
|
|
400
397
|
/**
|
|
401
|
-
* The y position of
|
|
398
|
+
* The y position of this vector.
|
|
402
399
|
*/
|
|
403
400
|
y;
|
|
404
401
|
constructor(x, y) {
|
|
@@ -406,11 +403,22 @@ class Vector2 {
|
|
|
406
403
|
this.y = y;
|
|
407
404
|
}
|
|
408
405
|
/**
|
|
409
|
-
* Multiplies
|
|
406
|
+
* Multiplies this vector with another vector.
|
|
407
|
+
*
|
|
408
|
+
* @param vec The other vector.
|
|
409
|
+
* @returns The multiplied vector.
|
|
410
410
|
*/
|
|
411
411
|
multiply(vec) {
|
|
412
412
|
return new Vector2(this.x * vec.x, this.y * vec.y);
|
|
413
413
|
}
|
|
414
|
+
/**
|
|
415
|
+
* Divides this vector with a scalar.
|
|
416
|
+
*
|
|
417
|
+
* Attempting to divide by 0 will throw an error.
|
|
418
|
+
*
|
|
419
|
+
* @param divideFactor The factor to divide the vector by.
|
|
420
|
+
* @returns The divided vector.
|
|
421
|
+
*/
|
|
414
422
|
divide(divideFactor) {
|
|
415
423
|
if (divideFactor === 0) {
|
|
416
424
|
throw new Error("Division by 0");
|
|
@@ -418,37 +426,52 @@ class Vector2 {
|
|
|
418
426
|
return new Vector2(this.x / divideFactor, this.y / divideFactor);
|
|
419
427
|
}
|
|
420
428
|
/**
|
|
421
|
-
* Adds
|
|
429
|
+
* Adds this vector with another vector.
|
|
430
|
+
*
|
|
431
|
+
* @param vec The other vector.
|
|
432
|
+
* @returns The added vector.
|
|
422
433
|
*/
|
|
423
434
|
add(vec) {
|
|
424
435
|
return new Vector2(this.x + vec.x, this.y + vec.y);
|
|
425
436
|
}
|
|
426
437
|
/**
|
|
427
|
-
* Subtracts
|
|
438
|
+
* Subtracts this vector with another vector.
|
|
439
|
+
*
|
|
440
|
+
* @param vec The other vector.
|
|
441
|
+
* @returns The subtracted vector.
|
|
428
442
|
*/
|
|
429
443
|
subtract(vec) {
|
|
430
444
|
return new Vector2(this.x - vec.x, this.y - vec.y);
|
|
431
445
|
}
|
|
432
446
|
/**
|
|
433
|
-
* The length of
|
|
447
|
+
* The length of this vector.
|
|
434
448
|
*/
|
|
435
449
|
get length() {
|
|
436
450
|
return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
|
|
437
451
|
}
|
|
438
452
|
/**
|
|
439
453
|
* Performs a dot multiplication with another vector.
|
|
454
|
+
*
|
|
455
|
+
* @param vec The other vector.
|
|
456
|
+
* @returns The dot product of both vectors.
|
|
440
457
|
*/
|
|
441
458
|
dot(vec) {
|
|
442
459
|
return this.x * vec.x + this.y * vec.y;
|
|
443
460
|
}
|
|
444
461
|
/**
|
|
445
|
-
* Scales
|
|
462
|
+
* Scales this vector.
|
|
463
|
+
*
|
|
464
|
+
* @param scaleFactor The factor to scale the vector by.
|
|
465
|
+
* @returns The scaled vector.
|
|
446
466
|
*/
|
|
447
467
|
scale(scaleFactor) {
|
|
448
468
|
return new Vector2(this.x * scaleFactor, this.y * scaleFactor);
|
|
449
469
|
}
|
|
450
470
|
/**
|
|
451
471
|
* Gets the distance between this vector and another vector.
|
|
472
|
+
*
|
|
473
|
+
* @param vec The other vector.
|
|
474
|
+
* @returns The distance between this vector and the other vector.
|
|
452
475
|
*/
|
|
453
476
|
getDistance(vec) {
|
|
454
477
|
const x = this.x - vec.x;
|
|
@@ -467,6 +490,7 @@ class Vector2 {
|
|
|
467
490
|
* Checks whether this vector is equal to another vector.
|
|
468
491
|
*
|
|
469
492
|
* @param other The other vector.
|
|
493
|
+
* @returns Whether this vector is equal to the other vector.
|
|
470
494
|
*/
|
|
471
495
|
equals(other) {
|
|
472
496
|
return this.x === other.x && this.y === other.y;
|
|
@@ -509,31 +533,6 @@ class HitObject {
|
|
|
509
533
|
get duration() {
|
|
510
534
|
return this.endTime - this.startTime;
|
|
511
535
|
}
|
|
512
|
-
/**
|
|
513
|
-
* The stacked position of the hitobject.
|
|
514
|
-
*/
|
|
515
|
-
get stackedPosition() {
|
|
516
|
-
if (this.type & exports.objectTypes.spinner) {
|
|
517
|
-
return this.position;
|
|
518
|
-
}
|
|
519
|
-
return this.position.add(this.stackOffset);
|
|
520
|
-
}
|
|
521
|
-
/**
|
|
522
|
-
* The stacked end position of the hitobject.
|
|
523
|
-
*/
|
|
524
|
-
get stackedEndPosition() {
|
|
525
|
-
if (this.type & exports.objectTypes.spinner) {
|
|
526
|
-
return this.position;
|
|
527
|
-
}
|
|
528
|
-
return this.endPosition.add(this.stackOffset);
|
|
529
|
-
}
|
|
530
|
-
/**
|
|
531
|
-
* The stack vector to calculate offset for stacked positions.
|
|
532
|
-
*/
|
|
533
|
-
get stackOffset() {
|
|
534
|
-
const coordinate = this.stackHeight * this.scale * -6.4;
|
|
535
|
-
return new Vector2(coordinate, coordinate);
|
|
536
|
-
}
|
|
537
536
|
/**
|
|
538
537
|
* Whether this hit object represents a new combo.
|
|
539
538
|
*/
|
|
@@ -554,15 +553,13 @@ class HitObject {
|
|
|
554
553
|
*/
|
|
555
554
|
stackHeight = 0;
|
|
556
555
|
/**
|
|
557
|
-
* The scale used to calculate stacked position and radius.
|
|
556
|
+
* The osu!droid scale used to calculate stacked position and radius.
|
|
558
557
|
*/
|
|
559
|
-
|
|
558
|
+
droidScale = 1;
|
|
560
559
|
/**
|
|
561
|
-
* The
|
|
560
|
+
* The osu!standard scale used to calculate stacked position and radius.
|
|
562
561
|
*/
|
|
563
|
-
|
|
564
|
-
return 64 * this.scale;
|
|
565
|
-
}
|
|
562
|
+
osuScale = 1;
|
|
566
563
|
constructor(values) {
|
|
567
564
|
this.startTime = values.startTime;
|
|
568
565
|
this.endTime = values.endTime ?? values.startTime;
|
|
@@ -572,6 +569,62 @@ class HitObject {
|
|
|
572
569
|
this.isNewCombo = values.newCombo ?? false;
|
|
573
570
|
this.comboOffset = values.comboOffset ?? 0;
|
|
574
571
|
}
|
|
572
|
+
/**
|
|
573
|
+
* Evaluates the radius of the hitobject.
|
|
574
|
+
*
|
|
575
|
+
* @param mode The gamemode to evaluate for.
|
|
576
|
+
* @returns The radius of the hitobject with respect to the gamemode.
|
|
577
|
+
*/
|
|
578
|
+
getRadius(mode) {
|
|
579
|
+
let radius = 64;
|
|
580
|
+
switch (mode) {
|
|
581
|
+
case exports.modes.droid:
|
|
582
|
+
radius *= this.droidScale;
|
|
583
|
+
break;
|
|
584
|
+
case exports.modes.osu:
|
|
585
|
+
radius *= this.osuScale;
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
return radius;
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Evaluates the stack offset vector of the hitobject.
|
|
592
|
+
*
|
|
593
|
+
* This is used to calculate offset for stacked positions.
|
|
594
|
+
*
|
|
595
|
+
* @param mode The gamemode to evaluate for.
|
|
596
|
+
* @returns The stack offset with respect to the gamemode.
|
|
597
|
+
*/
|
|
598
|
+
getStackOffset(mode) {
|
|
599
|
+
let coordinate = this.stackHeight * -6.4;
|
|
600
|
+
switch (mode) {
|
|
601
|
+
case exports.modes.droid:
|
|
602
|
+
coordinate *= this.droidScale;
|
|
603
|
+
break;
|
|
604
|
+
case exports.modes.osu:
|
|
605
|
+
coordinate *= this.osuScale;
|
|
606
|
+
break;
|
|
607
|
+
}
|
|
608
|
+
return new Vector2(coordinate, coordinate);
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Evaluates the stacked position of the hitobject.
|
|
612
|
+
*
|
|
613
|
+
* @param mode The gamemode to evaluate for.
|
|
614
|
+
* @returns The stacked position with respect to the gamemode.
|
|
615
|
+
*/
|
|
616
|
+
getStackedPosition(mode) {
|
|
617
|
+
return this.evaluateStackedPosition(this.position, mode);
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Evaluates the stacked end position of the hitobject.
|
|
621
|
+
*
|
|
622
|
+
* @param mode The gamemode to evaluate for.
|
|
623
|
+
* @returns The stacked end position with respect to the gamemode.
|
|
624
|
+
*/
|
|
625
|
+
getStackedEndPosition(mode) {
|
|
626
|
+
return this.evaluateStackedPosition(this.endPosition, mode);
|
|
627
|
+
}
|
|
575
628
|
/**
|
|
576
629
|
* Returns the hitobject type.
|
|
577
630
|
*/
|
|
@@ -588,6 +641,19 @@ class HitObject {
|
|
|
588
641
|
}
|
|
589
642
|
return res.substring(0, Math.max(0, res.length - 3));
|
|
590
643
|
}
|
|
644
|
+
/**
|
|
645
|
+
* Evaluates the stacked position of the specified position.
|
|
646
|
+
*
|
|
647
|
+
* @param position The position to evaluate.
|
|
648
|
+
* @param mode The gamemode to evaluate for.
|
|
649
|
+
* @returns The stacked position.
|
|
650
|
+
*/
|
|
651
|
+
evaluateStackedPosition(position, mode) {
|
|
652
|
+
if (this.type & exports.objectTypes.spinner) {
|
|
653
|
+
return position;
|
|
654
|
+
}
|
|
655
|
+
return position.add(this.getStackOffset(mode));
|
|
656
|
+
}
|
|
591
657
|
}
|
|
592
658
|
|
|
593
659
|
/**
|
|
@@ -1305,11 +1371,10 @@ class DifficultyControlPoint extends ControlPoint {
|
|
|
1305
1371
|
* This exists for backwards compatibility with maps that abuse NaN slider velocity behavior on osu!stable (e.g. /b/2628991).
|
|
1306
1372
|
*/
|
|
1307
1373
|
generateTicks;
|
|
1308
|
-
// Generate ticks can be made required in 3.0.
|
|
1309
1374
|
constructor(values) {
|
|
1310
1375
|
super(values);
|
|
1311
1376
|
this.speedMultiplier = values.speedMultiplier;
|
|
1312
|
-
this.generateTicks = values.generateTicks
|
|
1377
|
+
this.generateTicks = values.generateTicks;
|
|
1313
1378
|
}
|
|
1314
1379
|
isRedundant(existing) {
|
|
1315
1380
|
return (this.speedMultiplier === existing.speedMultiplier &&
|
|
@@ -1332,6 +1397,7 @@ class DifficultyControlPointManager extends ControlPointManager {
|
|
|
1332
1397
|
defaultControlPoint = new DifficultyControlPoint({
|
|
1333
1398
|
time: 0,
|
|
1334
1399
|
speedMultiplier: 1,
|
|
1400
|
+
generateTicks: true,
|
|
1335
1401
|
});
|
|
1336
1402
|
controlPointAt(time) {
|
|
1337
1403
|
return this.binarySearchWithFallback(time);
|
|
@@ -1725,7 +1791,12 @@ class Beatmap {
|
|
|
1725
1791
|
* @param stats The statistics used for calculation.
|
|
1726
1792
|
*/
|
|
1727
1793
|
maxDroidScore(stats) {
|
|
1728
|
-
let scoreMultiplier =
|
|
1794
|
+
let scoreMultiplier = 1;
|
|
1795
|
+
for (const mod of stats.mods) {
|
|
1796
|
+
if (mod.isApplicableToDroid()) {
|
|
1797
|
+
scoreMultiplier *= mod.droidScoreMultiplier;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1729
1800
|
const { speedMultiplier } = stats;
|
|
1730
1801
|
if (speedMultiplier >= 1) {
|
|
1731
1802
|
scoreMultiplier *= 1 + (speedMultiplier - 1) * 0.24;
|
|
@@ -1770,7 +1841,12 @@ class Beatmap {
|
|
|
1770
1841
|
maxOsuScore(mods = []) {
|
|
1771
1842
|
const accumulatedDiffPoints = this.difficulty.cs + this.difficulty.hp + this.difficulty.od;
|
|
1772
1843
|
let difficultyMultiplier = 2;
|
|
1773
|
-
|
|
1844
|
+
let scoreMultiplier = 1;
|
|
1845
|
+
for (const mod of mods) {
|
|
1846
|
+
if (mod.isApplicableToOsu()) {
|
|
1847
|
+
scoreMultiplier *= mod.pcScoreMultiplier;
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1774
1850
|
switch (true) {
|
|
1775
1851
|
case accumulatedDiffPoints <= 5:
|
|
1776
1852
|
difficultyMultiplier = 2;
|
|
@@ -1881,15 +1957,6 @@ class BeatmapBackground {
|
|
|
1881
1957
|
}
|
|
1882
1958
|
}
|
|
1883
1959
|
|
|
1884
|
-
/**
|
|
1885
|
-
* Mode enum to switch things between osu!droid and osu!standard.
|
|
1886
|
-
*/
|
|
1887
|
-
exports.modes = void 0;
|
|
1888
|
-
(function (modes) {
|
|
1889
|
-
modes["droid"] = "droid";
|
|
1890
|
-
modes["osu"] = "osu";
|
|
1891
|
-
})(exports.modes || (exports.modes = {}));
|
|
1892
|
-
|
|
1893
1960
|
class HitWindow {
|
|
1894
1961
|
/**
|
|
1895
1962
|
* The overall difficulty of this hit window.
|
|
@@ -2018,18 +2085,28 @@ class OsuHitWindow extends HitWindow {
|
|
|
2018
2085
|
}
|
|
2019
2086
|
}
|
|
2020
2087
|
|
|
2021
|
-
// TODO: separate droid/PC mod implementations
|
|
2022
2088
|
/**
|
|
2023
2089
|
* Represents a mod.
|
|
2024
2090
|
*/
|
|
2025
2091
|
class Mod {
|
|
2092
|
+
/**
|
|
2093
|
+
* Whether this mod can be applied to osu!droid.
|
|
2094
|
+
*/
|
|
2095
|
+
isApplicableToDroid() {
|
|
2096
|
+
return "droidRanked" in this;
|
|
2097
|
+
}
|
|
2098
|
+
/**
|
|
2099
|
+
* Whether this mod can be applied to osu!standard.
|
|
2100
|
+
*/
|
|
2101
|
+
isApplicableToOsu() {
|
|
2102
|
+
return "pcRanked" in this;
|
|
2103
|
+
}
|
|
2026
2104
|
}
|
|
2027
2105
|
|
|
2028
2106
|
/**
|
|
2029
2107
|
* Represents the DoubleTime mod.
|
|
2030
2108
|
*/
|
|
2031
2109
|
class ModDoubleTime extends Mod {
|
|
2032
|
-
scoreMultiplier = 1.12;
|
|
2033
2110
|
acronym = "DT";
|
|
2034
2111
|
name = "DoubleTime";
|
|
2035
2112
|
droidRanked = true;
|
|
@@ -2038,14 +2115,12 @@ class ModDoubleTime extends Mod {
|
|
|
2038
2115
|
pcScoreMultiplier = 1.12;
|
|
2039
2116
|
bitwise = 1 << 6;
|
|
2040
2117
|
droidString = "d";
|
|
2041
|
-
droidOnly = false;
|
|
2042
2118
|
}
|
|
2043
2119
|
|
|
2044
2120
|
/**
|
|
2045
2121
|
* Represents the HalfTime mod.
|
|
2046
2122
|
*/
|
|
2047
2123
|
class ModHalfTime extends Mod {
|
|
2048
|
-
scoreMultiplier = 0.3;
|
|
2049
2124
|
acronym = "HT";
|
|
2050
2125
|
name = "HalfTime";
|
|
2051
2126
|
droidRanked = true;
|
|
@@ -2054,14 +2129,12 @@ class ModHalfTime extends Mod {
|
|
|
2054
2129
|
pcScoreMultiplier = 0.3;
|
|
2055
2130
|
bitwise = 1 << 8;
|
|
2056
2131
|
droidString = "t";
|
|
2057
|
-
droidOnly = false;
|
|
2058
2132
|
}
|
|
2059
2133
|
|
|
2060
2134
|
/**
|
|
2061
2135
|
* Represents the NightCore mod.
|
|
2062
2136
|
*/
|
|
2063
2137
|
class ModNightCore extends Mod {
|
|
2064
|
-
scoreMultiplier = 1.12;
|
|
2065
2138
|
acronym = "NC";
|
|
2066
2139
|
name = "NightCore";
|
|
2067
2140
|
droidRanked = true;
|
|
@@ -2070,14 +2143,12 @@ class ModNightCore extends Mod {
|
|
|
2070
2143
|
pcScoreMultiplier = 1.12;
|
|
2071
2144
|
bitwise = 1 << 9;
|
|
2072
2145
|
droidString = "c";
|
|
2073
|
-
droidOnly = false;
|
|
2074
2146
|
}
|
|
2075
2147
|
|
|
2076
2148
|
/**
|
|
2077
2149
|
* Represents the HardRock mod.
|
|
2078
2150
|
*/
|
|
2079
2151
|
class ModHardRock extends Mod {
|
|
2080
|
-
scoreMultiplier = 1.06;
|
|
2081
2152
|
acronym = "HR";
|
|
2082
2153
|
name = "HardRock";
|
|
2083
2154
|
bitwise = 1 << 4;
|
|
@@ -2086,14 +2157,12 @@ class ModHardRock extends Mod {
|
|
|
2086
2157
|
droidScoreMultiplier = 1.06;
|
|
2087
2158
|
pcScoreMultiplier = 1.06;
|
|
2088
2159
|
droidString = "r";
|
|
2089
|
-
droidOnly = false;
|
|
2090
2160
|
}
|
|
2091
2161
|
|
|
2092
2162
|
/**
|
|
2093
2163
|
* Represents the Easy mod.
|
|
2094
2164
|
*/
|
|
2095
2165
|
class ModEasy extends Mod {
|
|
2096
|
-
scoreMultiplier = 0.5;
|
|
2097
2166
|
acronym = "EZ";
|
|
2098
2167
|
name = "Easy";
|
|
2099
2168
|
droidRanked = true;
|
|
@@ -2102,62 +2171,45 @@ class ModEasy extends Mod {
|
|
|
2102
2171
|
pcScoreMultiplier = 0.5;
|
|
2103
2172
|
bitwise = 1 << 1;
|
|
2104
2173
|
droidString = "e";
|
|
2105
|
-
droidOnly = false;
|
|
2106
2174
|
}
|
|
2107
2175
|
|
|
2108
2176
|
/**
|
|
2109
2177
|
* Represents the Precise mod.
|
|
2110
2178
|
*/
|
|
2111
2179
|
class ModPrecise extends Mod {
|
|
2112
|
-
scoreMultiplier = 1.06;
|
|
2113
2180
|
acronym = "PR";
|
|
2114
2181
|
name = "Precise";
|
|
2115
2182
|
droidRanked = true;
|
|
2116
|
-
pcRanked = false;
|
|
2117
2183
|
droidScoreMultiplier = 1.06;
|
|
2118
|
-
pcScoreMultiplier = 1.06;
|
|
2119
|
-
bitwise = Number.NaN;
|
|
2120
2184
|
droidString = "s";
|
|
2121
|
-
droidOnly = true;
|
|
2122
2185
|
}
|
|
2123
2186
|
|
|
2124
2187
|
/**
|
|
2125
2188
|
* Represents the SmallCircle mod.
|
|
2126
2189
|
*/
|
|
2127
2190
|
class ModSmallCircle extends Mod {
|
|
2128
|
-
scoreMultiplier = 1.06;
|
|
2129
2191
|
acronym = "SC";
|
|
2130
2192
|
name = "SmallCircle";
|
|
2131
2193
|
droidRanked = false;
|
|
2132
|
-
pcRanked = false;
|
|
2133
2194
|
droidScoreMultiplier = 1.06;
|
|
2134
|
-
pcScoreMultiplier = 1;
|
|
2135
|
-
bitwise = Number.NaN;
|
|
2136
2195
|
droidString = "m";
|
|
2137
|
-
droidOnly = true;
|
|
2138
2196
|
}
|
|
2139
2197
|
|
|
2140
2198
|
/**
|
|
2141
2199
|
* Represents the ReallyEasy mod.
|
|
2142
2200
|
*/
|
|
2143
2201
|
class ModReallyEasy extends Mod {
|
|
2144
|
-
scoreMultiplier = 0.4;
|
|
2145
2202
|
acronym = "RE";
|
|
2146
2203
|
name = "ReallyEasy";
|
|
2147
2204
|
droidRanked = false;
|
|
2148
|
-
pcRanked = false;
|
|
2149
2205
|
droidScoreMultiplier = 0.4;
|
|
2150
|
-
pcScoreMultiplier = 0.4;
|
|
2151
|
-
bitwise = Number.NaN;
|
|
2152
2206
|
droidString = "l";
|
|
2153
|
-
droidOnly = true;
|
|
2154
2207
|
}
|
|
2155
2208
|
|
|
2156
2209
|
/**
|
|
2157
2210
|
* Represents the Auto mod.
|
|
2158
2211
|
*/
|
|
2159
2212
|
class ModAuto extends Mod {
|
|
2160
|
-
scoreMultiplier = 0;
|
|
2161
2213
|
acronym = "AT";
|
|
2162
2214
|
name = "Autoplay";
|
|
2163
2215
|
droidRanked = false;
|
|
@@ -2166,14 +2218,12 @@ class ModAuto extends Mod {
|
|
|
2166
2218
|
pcScoreMultiplier = 1;
|
|
2167
2219
|
bitwise = 1 << 11;
|
|
2168
2220
|
droidString = "a";
|
|
2169
|
-
droidOnly = false;
|
|
2170
2221
|
}
|
|
2171
2222
|
|
|
2172
2223
|
/**
|
|
2173
2224
|
* Represents the Autopilot mod.
|
|
2174
2225
|
*/
|
|
2175
2226
|
class ModAutopilot extends Mod {
|
|
2176
|
-
scoreMultiplier = 0;
|
|
2177
2227
|
acronym = "AP";
|
|
2178
2228
|
name = "Autopilot";
|
|
2179
2229
|
droidRanked = false;
|
|
@@ -2182,14 +2232,12 @@ class ModAutopilot extends Mod {
|
|
|
2182
2232
|
pcScoreMultiplier = 0;
|
|
2183
2233
|
bitwise = 1 << 13;
|
|
2184
2234
|
droidString = "p";
|
|
2185
|
-
droidOnly = false;
|
|
2186
2235
|
}
|
|
2187
2236
|
|
|
2188
2237
|
/**
|
|
2189
2238
|
* Represents the Flashlight mod.
|
|
2190
2239
|
*/
|
|
2191
2240
|
class ModFlashlight extends Mod {
|
|
2192
|
-
scoreMultiplier = 1.12;
|
|
2193
2241
|
acronym = "FL";
|
|
2194
2242
|
name = "Flashlight";
|
|
2195
2243
|
droidRanked = false;
|
|
@@ -2198,7 +2246,6 @@ class ModFlashlight extends Mod {
|
|
|
2198
2246
|
pcScoreMultiplier = 1.12;
|
|
2199
2247
|
bitwise = 1 << 10;
|
|
2200
2248
|
droidString = "i";
|
|
2201
|
-
droidOnly = false;
|
|
2202
2249
|
}
|
|
2203
2250
|
|
|
2204
2251
|
/**
|
|
@@ -2207,7 +2254,6 @@ class ModFlashlight extends Mod {
|
|
|
2207
2254
|
class ModHidden extends Mod {
|
|
2208
2255
|
static fadeInDurationMultiplier = 0.4;
|
|
2209
2256
|
static fadeOutDurationMultiplier = 0.3;
|
|
2210
|
-
scoreMultiplier = 1.06;
|
|
2211
2257
|
acronym = "HD";
|
|
2212
2258
|
name = "Hidden";
|
|
2213
2259
|
bitwise = 1 << 3;
|
|
@@ -2216,14 +2262,12 @@ class ModHidden extends Mod {
|
|
|
2216
2262
|
droidScoreMultiplier = 1.06;
|
|
2217
2263
|
pcScoreMultiplier = 1.06;
|
|
2218
2264
|
droidString = "h";
|
|
2219
|
-
droidOnly = false;
|
|
2220
2265
|
}
|
|
2221
2266
|
|
|
2222
2267
|
/**
|
|
2223
2268
|
* Represents the NoFail mod.
|
|
2224
2269
|
*/
|
|
2225
2270
|
class ModNoFail extends Mod {
|
|
2226
|
-
scoreMultiplier = 0.5;
|
|
2227
2271
|
acronym = "NF";
|
|
2228
2272
|
name = "NoFail";
|
|
2229
2273
|
droidRanked = true;
|
|
@@ -2232,14 +2276,12 @@ class ModNoFail extends Mod {
|
|
|
2232
2276
|
pcScoreMultiplier = 0.5;
|
|
2233
2277
|
bitwise = 1 << 0;
|
|
2234
2278
|
droidString = "n";
|
|
2235
|
-
droidOnly = false;
|
|
2236
2279
|
}
|
|
2237
2280
|
|
|
2238
2281
|
/**
|
|
2239
2282
|
* Represents the Perfect mod.
|
|
2240
2283
|
*/
|
|
2241
2284
|
class ModPerfect extends Mod {
|
|
2242
|
-
scoreMultiplier = 1;
|
|
2243
2285
|
acronym = "PF";
|
|
2244
2286
|
name = "Perfect";
|
|
2245
2287
|
droidRanked = false;
|
|
@@ -2248,14 +2290,12 @@ class ModPerfect extends Mod {
|
|
|
2248
2290
|
pcScoreMultiplier = 1;
|
|
2249
2291
|
bitwise = 1 << 14;
|
|
2250
2292
|
droidString = "f";
|
|
2251
|
-
droidOnly = false;
|
|
2252
2293
|
}
|
|
2253
2294
|
|
|
2254
2295
|
/**
|
|
2255
2296
|
* Represents the Relax mod.
|
|
2256
2297
|
*/
|
|
2257
2298
|
class ModRelax extends Mod {
|
|
2258
|
-
scoreMultiplier = 0;
|
|
2259
2299
|
acronym = "RX";
|
|
2260
2300
|
name = "Relax";
|
|
2261
2301
|
droidRanked = false;
|
|
@@ -2264,14 +2304,12 @@ class ModRelax extends Mod {
|
|
|
2264
2304
|
pcScoreMultiplier = 0;
|
|
2265
2305
|
bitwise = 1 << 7;
|
|
2266
2306
|
droidString = "x";
|
|
2267
|
-
droidOnly = false;
|
|
2268
2307
|
}
|
|
2269
2308
|
|
|
2270
2309
|
/**
|
|
2271
2310
|
* Represents the ScoreV2 mod.
|
|
2272
2311
|
*/
|
|
2273
2312
|
class ModScoreV2 extends Mod {
|
|
2274
|
-
scoreMultiplier = 1;
|
|
2275
2313
|
acronym = "V2";
|
|
2276
2314
|
name = "ScoreV2";
|
|
2277
2315
|
droidRanked = false;
|
|
@@ -2280,30 +2318,23 @@ class ModScoreV2 extends Mod {
|
|
|
2280
2318
|
pcScoreMultiplier = 1;
|
|
2281
2319
|
bitwise = 1 << 29;
|
|
2282
2320
|
droidString = "v";
|
|
2283
|
-
droidOnly = false;
|
|
2284
2321
|
}
|
|
2285
2322
|
|
|
2286
2323
|
/**
|
|
2287
2324
|
* Represents the SpunOut mod.
|
|
2288
2325
|
*/
|
|
2289
2326
|
class ModSpunOut extends Mod {
|
|
2290
|
-
scoreMultiplier = 0.9;
|
|
2291
2327
|
acronym = "SO";
|
|
2292
2328
|
name = "SpunOut";
|
|
2293
|
-
droidRanked = false;
|
|
2294
2329
|
pcRanked = true;
|
|
2295
|
-
droidScoreMultiplier = 0.9;
|
|
2296
2330
|
pcScoreMultiplier = 0.9;
|
|
2297
2331
|
bitwise = 1 << 12;
|
|
2298
|
-
droidString = "";
|
|
2299
|
-
droidOnly = false;
|
|
2300
2332
|
}
|
|
2301
2333
|
|
|
2302
2334
|
/**
|
|
2303
2335
|
* Represents the SuddenDeath mod.
|
|
2304
2336
|
*/
|
|
2305
2337
|
class ModSuddenDeath extends Mod {
|
|
2306
|
-
scoreMultiplier = 1;
|
|
2307
2338
|
acronym = "SD";
|
|
2308
2339
|
name = "Sudden Death";
|
|
2309
2340
|
droidRanked = false;
|
|
@@ -2312,23 +2343,17 @@ class ModSuddenDeath extends Mod {
|
|
|
2312
2343
|
pcScoreMultiplier = 1;
|
|
2313
2344
|
bitwise = 1 << 5;
|
|
2314
2345
|
droidString = "u";
|
|
2315
|
-
droidOnly = false;
|
|
2316
2346
|
}
|
|
2317
2347
|
|
|
2318
2348
|
/**
|
|
2319
2349
|
* Represents the TouchDevice mod.
|
|
2320
2350
|
*/
|
|
2321
2351
|
class ModTouchDevice extends Mod {
|
|
2322
|
-
scoreMultiplier = 1;
|
|
2323
2352
|
acronym = "TD";
|
|
2324
2353
|
name = "TouchDevice";
|
|
2325
|
-
droidRanked = true;
|
|
2326
2354
|
pcRanked = true;
|
|
2327
|
-
droidScoreMultiplier = 1;
|
|
2328
2355
|
pcScoreMultiplier = 1;
|
|
2329
2356
|
bitwise = 1 << 2;
|
|
2330
|
-
droidString = "";
|
|
2331
|
-
droidOnly = false;
|
|
2332
2357
|
}
|
|
2333
2358
|
|
|
2334
2359
|
/**
|
|
@@ -2393,7 +2418,8 @@ class ModUtil {
|
|
|
2393
2418
|
* @param options Options for parsing behavior.
|
|
2394
2419
|
*/
|
|
2395
2420
|
static droidStringToMods(str, options) {
|
|
2396
|
-
return this.processParsingOptions(this.allMods.filter((m) => m.
|
|
2421
|
+
return this.processParsingOptions(this.allMods.filter((m) => m.isApplicableToDroid() &&
|
|
2422
|
+
str.toLowerCase().includes(m.droidString)), options);
|
|
2397
2423
|
}
|
|
2398
2424
|
/**
|
|
2399
2425
|
* Gets a list of mods from a PC modbits.
|
|
@@ -2402,7 +2428,7 @@ class ModUtil {
|
|
|
2402
2428
|
* @param options Options for parsing behavior.
|
|
2403
2429
|
*/
|
|
2404
2430
|
static pcModbitsToMods(modbits, options) {
|
|
2405
|
-
return this.processParsingOptions(this.allMods.filter((m) => m.bitwise & modbits), options);
|
|
2431
|
+
return this.processParsingOptions(this.allMods.filter((m) => m.isApplicableToOsu() && m.bitwise & modbits), options);
|
|
2406
2432
|
}
|
|
2407
2433
|
/**
|
|
2408
2434
|
* Gets a list of mods from a PC mod string, such as "HDHR".
|
|
@@ -2443,11 +2469,9 @@ class ModUtil {
|
|
|
2443
2469
|
*/
|
|
2444
2470
|
static checkIncompatibleMods(mods) {
|
|
2445
2471
|
for (const incompatibleMod of this.incompatibleMods) {
|
|
2446
|
-
const fulfilledMods = mods.filter((m) => incompatibleMod.
|
|
2472
|
+
const fulfilledMods = mods.filter((m) => incompatibleMod.some((v) => m.acronym === v.acronym));
|
|
2447
2473
|
if (fulfilledMods.length > 1) {
|
|
2448
|
-
mods = mods.filter((m) =>
|
|
2449
|
-
.map((v) => v.acronym)
|
|
2450
|
-
.includes(m.acronym));
|
|
2474
|
+
mods = mods.filter((m) => incompatibleMod.every((v) => m.acronym !== v.acronym));
|
|
2451
2475
|
// Keep the first selected mod
|
|
2452
2476
|
mods.push(fulfilledMods[0]);
|
|
2453
2477
|
}
|
|
@@ -4332,7 +4356,12 @@ class RGBColor {
|
|
|
4332
4356
|
* Returns a string representation of the color.
|
|
4333
4357
|
*/
|
|
4334
4358
|
toString() {
|
|
4335
|
-
|
|
4359
|
+
if (this.a === 1) {
|
|
4360
|
+
return `${this.r},${this.g},${this.b}`;
|
|
4361
|
+
}
|
|
4362
|
+
else {
|
|
4363
|
+
return `${this.r},${this.g},${this.b},${this.a}`;
|
|
4364
|
+
}
|
|
4336
4365
|
}
|
|
4337
4366
|
/**
|
|
4338
4367
|
* Checks whether this color is equal to another color.
|
|
@@ -4340,7 +4369,10 @@ class RGBColor {
|
|
|
4340
4369
|
* @param other The other color.
|
|
4341
4370
|
*/
|
|
4342
4371
|
equals(other) {
|
|
4343
|
-
return this.r === other.r &&
|
|
4372
|
+
return (this.r === other.r &&
|
|
4373
|
+
this.g === other.g &&
|
|
4374
|
+
this.b === other.b &&
|
|
4375
|
+
this.a === other.a);
|
|
4344
4376
|
}
|
|
4345
4377
|
}
|
|
4346
4378
|
|
|
@@ -4694,21 +4726,11 @@ class CommandTimelineGroup {
|
|
|
4694
4726
|
this.flipHorizontal,
|
|
4695
4727
|
this.flipVertical,
|
|
4696
4728
|
];
|
|
4697
|
-
/**
|
|
4698
|
-
* The earliest visible time. Will be `null` unless this group's first alpha command has a start value of zero.
|
|
4699
|
-
*/
|
|
4700
|
-
get earliestDisplayedTime() {
|
|
4701
|
-
const first = this.alpha.commands.at(0);
|
|
4702
|
-
return first?.startValue === 0 ? first.startTime : null;
|
|
4703
|
-
}
|
|
4704
4729
|
/**
|
|
4705
4730
|
* The start time of commands.
|
|
4706
4731
|
*/
|
|
4707
4732
|
get commandsStartTime() {
|
|
4708
|
-
|
|
4709
|
-
// This is due to it creating a state where the target is not present before that time, causing any other events to not be visible.
|
|
4710
|
-
return (this.earliestDisplayedTime ??
|
|
4711
|
-
Math.min(...this.timelines.map((t) => t.startTime)));
|
|
4733
|
+
return Math.min(...this.timelines.map((t) => t.startTime));
|
|
4712
4734
|
}
|
|
4713
4735
|
/**
|
|
4714
4736
|
* The end time of commands.
|
|
@@ -4749,7 +4771,7 @@ class CommandTimelineGroup {
|
|
|
4749
4771
|
/**
|
|
4750
4772
|
* Gets the commands from a command timeline.
|
|
4751
4773
|
*
|
|
4752
|
-
* @param timelineSelector A function
|
|
4774
|
+
* @param timelineSelector A function to select the command timeline to retrieve commands from.
|
|
4753
4775
|
* @param offset The offset to apply to all commands.
|
|
4754
4776
|
*/
|
|
4755
4777
|
getCommands(timelineSelector, offset = 0) {
|
|
@@ -4861,20 +4883,14 @@ class StoryboardElement {
|
|
|
4861
4883
|
* Represents a storyboard sprite.
|
|
4862
4884
|
*/
|
|
4863
4885
|
class StoryboardSprite extends StoryboardElement {
|
|
4864
|
-
_loops = [];
|
|
4865
|
-
_triggers = [];
|
|
4866
4886
|
/**
|
|
4867
4887
|
* The loop commands of the sprite.
|
|
4868
4888
|
*/
|
|
4869
|
-
|
|
4870
|
-
return this._loops;
|
|
4871
|
-
}
|
|
4889
|
+
loops = [];
|
|
4872
4890
|
/**
|
|
4873
4891
|
* The trigger commands of the sprite.
|
|
4874
4892
|
*/
|
|
4875
|
-
|
|
4876
|
-
return this._triggers;
|
|
4877
|
-
}
|
|
4893
|
+
triggers = [];
|
|
4878
4894
|
/**
|
|
4879
4895
|
* The origin of the sprite.
|
|
4880
4896
|
*/
|
|
@@ -4888,28 +4904,51 @@ class StoryboardSprite extends StoryboardElement {
|
|
|
4888
4904
|
*/
|
|
4889
4905
|
timelineGroup = new CommandTimelineGroup();
|
|
4890
4906
|
get startTime() {
|
|
4891
|
-
//
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4907
|
+
// To get the initial start time, we need to check whether the first alpha command to exist (across all loops) has a start value of zero.
|
|
4908
|
+
// A start value of zero governs, above all else, the first valid display time of a sprite.
|
|
4909
|
+
//
|
|
4910
|
+
// You can imagine that the first command of each type decides that type's start value, so if the initial alpha is zero,
|
|
4911
|
+
// anything before that point can be ignored (the sprite is not visible after all).
|
|
4912
|
+
const alphaCommands = [];
|
|
4913
|
+
let command = this.timelineGroup.alpha.commands[0];
|
|
4914
|
+
if (command) {
|
|
4915
|
+
alphaCommands.push({
|
|
4916
|
+
startTime: command.startTime,
|
|
4917
|
+
isZeroStartValue: command.startTime === 0,
|
|
4918
|
+
});
|
|
4919
|
+
}
|
|
4920
|
+
for (const l of this.loops) {
|
|
4921
|
+
command = l.alpha.commands[0];
|
|
4922
|
+
if (command) {
|
|
4923
|
+
alphaCommands.push({
|
|
4924
|
+
startTime: command.startTime + l.loopStartTime,
|
|
4925
|
+
isZeroStartValue: command.startTime === 0,
|
|
4926
|
+
});
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
if (alphaCommands.length > 0) {
|
|
4930
|
+
const firstAlpha = alphaCommands.sort((a, b) => a.startTime - b.startTime)[0];
|
|
4931
|
+
if (firstAlpha.isZeroStartValue) {
|
|
4932
|
+
return firstAlpha.startTime;
|
|
4898
4933
|
}
|
|
4899
4934
|
}
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4935
|
+
// If we got to this point, either no alpha commands were present, or the earliest had a non-zero start value.
|
|
4936
|
+
// The sprite's start time will be determined by the earliest command, regardless of type.
|
|
4937
|
+
let earliestStartTime = this.timelineGroup.startTime;
|
|
4938
|
+
for (const l of this.loops) {
|
|
4939
|
+
earliestStartTime = Math.min(earliestStartTime, l.startTime);
|
|
4940
|
+
}
|
|
4941
|
+
return earliestStartTime;
|
|
4903
4942
|
}
|
|
4904
4943
|
get endTime() {
|
|
4905
|
-
return Math.max(this.timelineGroup.endTime, ...this.
|
|
4944
|
+
return Math.max(this.timelineGroup.endTime, ...this.loops.map((l) => l.endTime));
|
|
4906
4945
|
}
|
|
4907
4946
|
/**
|
|
4908
4947
|
* Whether this sprite has at least one command.
|
|
4909
4948
|
*/
|
|
4910
4949
|
get hasCommands() {
|
|
4911
4950
|
return (this.timelineGroup.hasCommands ||
|
|
4912
|
-
this.
|
|
4951
|
+
this.loops.some((l) => l.hasCommands));
|
|
4913
4952
|
}
|
|
4914
4953
|
constructor(path, origin, initialPosition) {
|
|
4915
4954
|
super(path);
|
|
@@ -4925,7 +4964,7 @@ class StoryboardSprite extends StoryboardElement {
|
|
|
4925
4964
|
*/
|
|
4926
4965
|
addLoop(startTime, repeatCount) {
|
|
4927
4966
|
const loop = new CommandLoop(startTime, repeatCount);
|
|
4928
|
-
this.
|
|
4967
|
+
this.loops.push(loop);
|
|
4929
4968
|
return loop;
|
|
4930
4969
|
}
|
|
4931
4970
|
/**
|
|
@@ -4939,7 +4978,7 @@ class StoryboardSprite extends StoryboardElement {
|
|
|
4939
4978
|
*/
|
|
4940
4979
|
addTrigger(triggerName, startTime, endTime, groupNumber) {
|
|
4941
4980
|
const trigger = new CommandTrigger(triggerName, startTime, endTime, groupNumber);
|
|
4942
|
-
this.
|
|
4981
|
+
this.triggers.push(trigger);
|
|
4943
4982
|
return trigger;
|
|
4944
4983
|
}
|
|
4945
4984
|
toString() {
|
|
@@ -5379,16 +5418,23 @@ class BeatmapDecoder extends Decoder {
|
|
|
5379
5418
|
else {
|
|
5380
5419
|
hitObjectsDecoder.applyStackingOld();
|
|
5381
5420
|
}
|
|
5382
|
-
const
|
|
5421
|
+
const droidCircleSize = new MapStats({
|
|
5422
|
+
cs: this.finalResult.difficulty.cs,
|
|
5423
|
+
mods,
|
|
5424
|
+
}).calculate({ mode: exports.modes.droid }).cs;
|
|
5425
|
+
const droidScale = (1 - (0.7 * (droidCircleSize - 5)) / 5) / 2;
|
|
5426
|
+
const osuCircleSize = new MapStats({
|
|
5383
5427
|
cs: this.finalResult.difficulty.cs,
|
|
5384
5428
|
mods,
|
|
5385
|
-
}).calculate().cs;
|
|
5386
|
-
const
|
|
5429
|
+
}).calculate({ mode: exports.modes.osu }).cs;
|
|
5430
|
+
const osuScale = (1 - (0.7 * (osuCircleSize - 5)) / 5) / 2;
|
|
5387
5431
|
this.finalResult.hitObjects.objects.forEach((h) => {
|
|
5388
|
-
h.
|
|
5432
|
+
h.droidScale = droidScale;
|
|
5433
|
+
h.osuScale = osuScale;
|
|
5389
5434
|
if (h instanceof Slider) {
|
|
5390
5435
|
h.nestedHitObjects.forEach((n) => {
|
|
5391
|
-
n.
|
|
5436
|
+
n.droidScale = droidScale;
|
|
5437
|
+
n.osuScale = osuScale;
|
|
5392
5438
|
});
|
|
5393
5439
|
}
|
|
5394
5440
|
});
|