@rian8337/osu-base 4.0.0-beta.85 → 4.0.0-beta.87
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 +803 -267
- package/package.json +5 -5
- package/typings/index.d.ts +1397 -1201
package/dist/index.js
CHANGED
|
@@ -132,6 +132,16 @@ class MathUtils {
|
|
|
132
132
|
static logistic(exponent, maxValue = 1) {
|
|
133
133
|
return maxValue / (1 + Math.exp(exponent));
|
|
134
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Calculates the p-norm of an n-dimensional vector.
|
|
137
|
+
*
|
|
138
|
+
* @param p The order of the norm.
|
|
139
|
+
* @param coefficients The coefficients of the vector.
|
|
140
|
+
* @returns The p-norm of the vector.
|
|
141
|
+
*/
|
|
142
|
+
static norm(p, ...coefficients) {
|
|
143
|
+
return Math.pow(coefficients.reduce((a, v) => a + Math.pow(v, p), 0), 1 / p);
|
|
144
|
+
}
|
|
135
145
|
/**
|
|
136
146
|
* Calculates an S-shaped {@link https://en.wikipedia.org/wiki/Logistic_function logistic function}
|
|
137
147
|
* with offset at `x`.
|
|
@@ -404,7 +414,7 @@ class BankHitSampleInfo extends HitSampleInfo {
|
|
|
404
414
|
break;
|
|
405
415
|
}
|
|
406
416
|
if (this.customSampleBank >= 2) {
|
|
407
|
-
names.push(`${prefix}-${this.name}${this.customSampleBank}`);
|
|
417
|
+
names.push(`${prefix}-${this.name}${this.customSampleBank.toString()}`);
|
|
408
418
|
}
|
|
409
419
|
names.push(`${prefix}-${this.name}`, this.name);
|
|
410
420
|
return names;
|
|
@@ -573,6 +583,7 @@ class PreciseDroidHitWindow extends HitWindow {
|
|
|
573
583
|
}
|
|
574
584
|
}
|
|
575
585
|
|
|
586
|
+
/* eslint-disable @typescript-eslint/prefer-literal-enum-member */
|
|
576
587
|
/**
|
|
577
588
|
* Bitmask constant of object types. This is needed as osu! uses bits to determine object types.
|
|
578
589
|
*/
|
|
@@ -713,7 +724,7 @@ class Vector2 {
|
|
|
713
724
|
* Returns a string representation of the vector.
|
|
714
725
|
*/
|
|
715
726
|
toString() {
|
|
716
|
-
return `${this.x},${this.y}`;
|
|
727
|
+
return `${this.x.toString()},${this.y.toString()}`;
|
|
717
728
|
}
|
|
718
729
|
}
|
|
719
730
|
|
|
@@ -741,6 +752,7 @@ class BeatmapDifficulty {
|
|
|
741
752
|
* @param min Minimum of the resulting range which will be achieved by a difficulty value of 0.
|
|
742
753
|
* @param mid Midpoint of the resulting range which will be achieved by a difficulty value of 5.
|
|
743
754
|
* @param max Maximum of the resulting range which will be achieved by a difficulty value of 10.
|
|
755
|
+
* @returns The value to which the difficulty value maps in the specified range.
|
|
744
756
|
*/
|
|
745
757
|
static difficultyRange(difficulty, min, mid, max) {
|
|
746
758
|
switch (true) {
|
|
@@ -752,6 +764,19 @@ class BeatmapDifficulty {
|
|
|
752
764
|
return mid;
|
|
753
765
|
}
|
|
754
766
|
}
|
|
767
|
+
/**
|
|
768
|
+
* Maps a difficulty value [0, 10] to a two-piece linear range of values. Floors the value to an integer,
|
|
769
|
+
* usually to match osu!stable specifications.
|
|
770
|
+
*
|
|
771
|
+
* @param difficulty The difficulty value to be mapped.
|
|
772
|
+
* @param min Minimum of the resulting range which will be achieved by a difficulty value of 0.
|
|
773
|
+
* @param mid Midpoint of the resulting range which will be achieved by a difficulty value of 5.
|
|
774
|
+
* @param max Maximum of the resulting range which will be achieved by a difficulty value of 10.
|
|
775
|
+
* @returns The value to which the difficulty value maps in the specified range.
|
|
776
|
+
*/
|
|
777
|
+
static difficultyRangeInt(difficulty, min, mid, max) {
|
|
778
|
+
return Math.trunc(BeatmapDifficulty.difficultyRange(difficulty, min, mid, max));
|
|
779
|
+
}
|
|
755
780
|
/**
|
|
756
781
|
* Inverse function to `difficultyRange`. Maps a value returned by the function back to the
|
|
757
782
|
* difficulty that produced it.
|
|
@@ -1169,7 +1194,7 @@ class HitObject {
|
|
|
1169
1194
|
if (this.hitWindow) {
|
|
1170
1195
|
this.hitWindow.overallDifficulty = difficulty.od;
|
|
1171
1196
|
}
|
|
1172
|
-
this.timePreempt = BeatmapDifficulty.
|
|
1197
|
+
this.timePreempt = BeatmapDifficulty.difficultyRangeInt(difficulty.ar, HitObject.preemptMax, HitObject.preemptMid, HitObject.preemptMin);
|
|
1173
1198
|
// Preempt time can go below 450ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR.
|
|
1174
1199
|
// This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear function above.
|
|
1175
1200
|
// Note that this doesn't exactly match the AR>10 visuals as they're classically known, but it feels good.
|
|
@@ -1370,6 +1395,7 @@ class ModSetting {
|
|
|
1370
1395
|
/**
|
|
1371
1396
|
* The formatter to display the value of this `ModSetting`.
|
|
1372
1397
|
*/
|
|
1398
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
1373
1399
|
this.displayFormatter = (v) => `${v}`;
|
|
1374
1400
|
this.valueChangedListeners = new Set();
|
|
1375
1401
|
this.name = name;
|
|
@@ -1618,7 +1644,7 @@ class SliderNestedHitObject extends HitObject {
|
|
|
1618
1644
|
this.spanStartTime = values.spanStartTime;
|
|
1619
1645
|
}
|
|
1620
1646
|
toString() {
|
|
1621
|
-
return `Position: [${this.
|
|
1647
|
+
return `Position: [${this.position.x.toString()}, ${this.position.y.toString()}], span index: ${this.spanIndex.toString()}, span start time: ${this.spanStartTime.toString()}`;
|
|
1622
1648
|
}
|
|
1623
1649
|
}
|
|
1624
1650
|
|
|
@@ -2016,7 +2042,9 @@ class Slider extends HitObject {
|
|
|
2016
2042
|
this.tickDistanceMultiplier
|
|
2017
2043
|
: Number.POSITIVE_INFINITY;
|
|
2018
2044
|
this.createNestedHitObjects(controlPoints);
|
|
2019
|
-
this.nestedHitObjects.forEach((v) =>
|
|
2045
|
+
this.nestedHitObjects.forEach((v) => {
|
|
2046
|
+
v.applyDefaults(controlPoints, difficulty, mode);
|
|
2047
|
+
});
|
|
2020
2048
|
}
|
|
2021
2049
|
applySamples(controlPoints) {
|
|
2022
2050
|
super.applySamples(controlPoints);
|
|
@@ -2219,7 +2247,7 @@ class Slider extends HitObject {
|
|
|
2219
2247
|
});
|
|
2220
2248
|
}
|
|
2221
2249
|
toString() {
|
|
2222
|
-
return `Position: [${this.position.x}, ${this.position.y}], distance: ${this.path.expectedDistance}, repeat count: ${this.repeatCount}, slider ticks: ${this.
|
|
2250
|
+
return `Position: [${this.position.x.toString()}, ${this.position.y.toString()}], distance: ${this.path.expectedDistance.toString()}, repeat count: ${this.repeatCount.toString()}, slider ticks: ${this.ticks.toString()}`;
|
|
2223
2251
|
}
|
|
2224
2252
|
}
|
|
2225
2253
|
Slider.baseNormalSlideSample = new BankHitSampleInfo("sliderslide");
|
|
@@ -2227,43 +2255,6 @@ Slider.baseWhistleSlideSample = new BankHitSampleInfo("sliderwhistle");
|
|
|
2227
2255
|
Slider.baseTickSample = new BankHitSampleInfo("slidertick");
|
|
2228
2256
|
Slider.legacyLastTickOffset = 36;
|
|
2229
2257
|
|
|
2230
|
-
/**
|
|
2231
|
-
* Represents the Freeze Frame mod.
|
|
2232
|
-
*/
|
|
2233
|
-
class ModFreezeFrame extends Mod {
|
|
2234
|
-
constructor() {
|
|
2235
|
-
super();
|
|
2236
|
-
this.name = "Freeze Frame";
|
|
2237
|
-
this.acronym = "FR";
|
|
2238
|
-
this.droidRanked = false;
|
|
2239
|
-
this.isDroidRelevant = true;
|
|
2240
|
-
this.droidScoreMultiplier = 1;
|
|
2241
|
-
this.osuRanked = false;
|
|
2242
|
-
this.isOsuRelevant = true;
|
|
2243
|
-
this.osuScoreMultiplier = 1;
|
|
2244
|
-
this.lastNewComboTime = 0;
|
|
2245
|
-
this.incompatibleMods.add(ModApproachDifferent);
|
|
2246
|
-
}
|
|
2247
|
-
applyToBeatmap(beatmap) {
|
|
2248
|
-
this.lastNewComboTime = 0;
|
|
2249
|
-
for (const hitObject of beatmap.hitObjects) {
|
|
2250
|
-
if (hitObject.isNewCombo) {
|
|
2251
|
-
this.lastNewComboTime = hitObject.startTime;
|
|
2252
|
-
}
|
|
2253
|
-
this.applyFadeInAdjustment(hitObject);
|
|
2254
|
-
}
|
|
2255
|
-
}
|
|
2256
|
-
applyFadeInAdjustment(hitObject) {
|
|
2257
|
-
hitObject.timePreempt += hitObject.startTime - this.lastNewComboTime;
|
|
2258
|
-
if (hitObject instanceof Slider) {
|
|
2259
|
-
// Freezing slider ticks doesn't play well with snaking sliders, and slider repeats will not
|
|
2260
|
-
// layer correctly if its preempt is changed.
|
|
2261
|
-
this.applyFadeInAdjustment(hitObject.head);
|
|
2262
|
-
this.applyFadeInAdjustment(hitObject.tail);
|
|
2263
|
-
}
|
|
2264
|
-
}
|
|
2265
|
-
}
|
|
2266
|
-
|
|
2267
2258
|
/**
|
|
2268
2259
|
* Represents the Traceable mod.
|
|
2269
2260
|
*/
|
|
@@ -2315,7 +2306,10 @@ class ModHidden extends Mod {
|
|
|
2315
2306
|
* The main object body will not fade when enabled.
|
|
2316
2307
|
*/
|
|
2317
2308
|
this.onlyFadeApproachCircles = new BooleanModSetting("Only fade approach circles", "The main object body will not fade when enabled.", false);
|
|
2318
|
-
this.incompatibleMods
|
|
2309
|
+
this.incompatibleMods
|
|
2310
|
+
.add(ModTraceable)
|
|
2311
|
+
.add(ModApproachDifferent)
|
|
2312
|
+
.add(ModFreezeFrame);
|
|
2319
2313
|
}
|
|
2320
2314
|
get droidScoreMultiplier() {
|
|
2321
2315
|
return this.usesDefaultSettings ? 1.06 : 1;
|
|
@@ -2354,6 +2348,43 @@ class ModHidden extends Mod {
|
|
|
2354
2348
|
ModHidden.fadeInDurationMultiplier = 0.4;
|
|
2355
2349
|
ModHidden.fadeOutDurationMultiplier = 0.3;
|
|
2356
2350
|
|
|
2351
|
+
/**
|
|
2352
|
+
* Represents the Freeze Frame mod.
|
|
2353
|
+
*/
|
|
2354
|
+
class ModFreezeFrame extends Mod {
|
|
2355
|
+
constructor() {
|
|
2356
|
+
super();
|
|
2357
|
+
this.name = "Freeze Frame";
|
|
2358
|
+
this.acronym = "FR";
|
|
2359
|
+
this.droidRanked = false;
|
|
2360
|
+
this.isDroidRelevant = true;
|
|
2361
|
+
this.droidScoreMultiplier = 1;
|
|
2362
|
+
this.osuRanked = false;
|
|
2363
|
+
this.isOsuRelevant = true;
|
|
2364
|
+
this.osuScoreMultiplier = 1;
|
|
2365
|
+
this.lastNewComboTime = 0;
|
|
2366
|
+
this.incompatibleMods.add(ModApproachDifferent).add(ModHidden);
|
|
2367
|
+
}
|
|
2368
|
+
applyToBeatmap(beatmap) {
|
|
2369
|
+
this.lastNewComboTime = 0;
|
|
2370
|
+
for (const hitObject of beatmap.hitObjects) {
|
|
2371
|
+
if (hitObject.isNewCombo) {
|
|
2372
|
+
this.lastNewComboTime = hitObject.startTime;
|
|
2373
|
+
}
|
|
2374
|
+
this.applyFadeInAdjustment(hitObject);
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
applyFadeInAdjustment(hitObject) {
|
|
2378
|
+
hitObject.timePreempt += hitObject.startTime - this.lastNewComboTime;
|
|
2379
|
+
if (hitObject instanceof Slider) {
|
|
2380
|
+
// Freezing slider ticks doesn't play well with snaking sliders, and slider repeats will not
|
|
2381
|
+
// layer correctly if its preempt is changed.
|
|
2382
|
+
this.applyFadeInAdjustment(hitObject.head);
|
|
2383
|
+
this.applyFadeInAdjustment(hitObject.tail);
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2357
2388
|
/**
|
|
2358
2389
|
* Represents a `Mod` specific setting that is constrained to a range of values.
|
|
2359
2390
|
*/
|
|
@@ -2410,13 +2441,13 @@ class NumberModSetting extends RangeConstrainedModSetting {
|
|
|
2410
2441
|
super(name, description, defaultValue, min, max, step);
|
|
2411
2442
|
this.displayFormatter = (v) => v.toString();
|
|
2412
2443
|
if (min > max) {
|
|
2413
|
-
throw new RangeError(`The minimum value (${min}) must be less than or equal to the maximum value (${max}).`);
|
|
2444
|
+
throw new RangeError(`The minimum value (${min.toString()}) must be less than or equal to the maximum value (${max.toString()}).`);
|
|
2414
2445
|
}
|
|
2415
2446
|
if (step < 0) {
|
|
2416
|
-
throw new RangeError(`The step size (${step}) must be greater than or equal to 0.`);
|
|
2447
|
+
throw new RangeError(`The step size (${step.toString()}) must be greater than or equal to 0.`);
|
|
2417
2448
|
}
|
|
2418
2449
|
if (defaultValue < min || defaultValue > max) {
|
|
2419
|
-
throw new RangeError(`The default value (${defaultValue}) must be between the minimum (${min}) and maximum (${max}) values.`);
|
|
2450
|
+
throw new RangeError(`The default value (${defaultValue.toString()}) must be between the minimum (${min.toString()}) and maximum (${max.toString()}) values.`);
|
|
2420
2451
|
}
|
|
2421
2452
|
}
|
|
2422
2453
|
processValue(value) {
|
|
@@ -2438,7 +2469,7 @@ class DecimalModSetting extends NumberModSetting {
|
|
|
2438
2469
|
}
|
|
2439
2470
|
set precision(value) {
|
|
2440
2471
|
if (value !== null && value < 0) {
|
|
2441
|
-
throw new RangeError(`The precision (${value}) must be greater than or equal to 0.`);
|
|
2472
|
+
throw new RangeError(`The precision (${value.toString()}) must be greater than or equal to 0.`);
|
|
2442
2473
|
}
|
|
2443
2474
|
this._precision = value;
|
|
2444
2475
|
if (value !== null) {
|
|
@@ -2454,7 +2485,7 @@ class DecimalModSetting extends NumberModSetting {
|
|
|
2454
2485
|
return super.toDisplayString();
|
|
2455
2486
|
};
|
|
2456
2487
|
if (precision !== null && precision < 0) {
|
|
2457
|
-
throw new RangeError(`The precision (${precision}) must be greater than or equal to 0.`);
|
|
2488
|
+
throw new RangeError(`The precision (${precision.toString()}) must be greater than or equal to 0.`);
|
|
2458
2489
|
}
|
|
2459
2490
|
this._precision = precision;
|
|
2460
2491
|
}
|
|
@@ -2807,11 +2838,8 @@ class ModDeflate extends ModObjectScaleTween {
|
|
|
2807
2838
|
* rectangle).
|
|
2808
2839
|
*/
|
|
2809
2840
|
class Circle extends HitObject {
|
|
2810
|
-
constructor(values) {
|
|
2811
|
-
super(values);
|
|
2812
|
-
}
|
|
2813
2841
|
toString() {
|
|
2814
|
-
return `Position: [${this.
|
|
2842
|
+
return `Position: [${this.position.x.toString()}, ${this.position.y.toString()}]`;
|
|
2815
2843
|
}
|
|
2816
2844
|
}
|
|
2817
2845
|
|
|
@@ -2862,7 +2890,7 @@ class Spinner extends HitObject {
|
|
|
2862
2890
|
return new EmptyHitWindow();
|
|
2863
2891
|
}
|
|
2864
2892
|
toString() {
|
|
2865
|
-
return `Position: [${this.
|
|
2893
|
+
return `Position: [${this.position.x.toString()}, ${this.position.y.toString()}], duration: ${this.duration.toString()}`;
|
|
2866
2894
|
}
|
|
2867
2895
|
}
|
|
2868
2896
|
Spinner.baseSpinnerSpinSample = new BankHitSampleInfo("spinnerspin");
|
|
@@ -2992,26 +3020,15 @@ class HitObjectPositionInfo {
|
|
|
2992
3020
|
* Precision utilities.
|
|
2993
3021
|
*/
|
|
2994
3022
|
class Precision {
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
}
|
|
3005
|
-
/**
|
|
3006
|
-
* Checks if two vectors are equal with a given tolerance.
|
|
3007
|
-
*
|
|
3008
|
-
* @param vec1 The first vector.
|
|
3009
|
-
* @param vec2 The second vector.
|
|
3010
|
-
* @param acceptableDifference The acceptable difference as threshold. Default is `Precision.FLOAT_EPSILON = 1e-3`.
|
|
3011
|
-
*/
|
|
3012
|
-
static almostEqualsVector(vec1, vec2, acceptableDifference = this.FLOAT_EPSILON) {
|
|
3013
|
-
return (this.almostEqualsNumber(vec1.x, vec2.x, acceptableDifference) &&
|
|
3014
|
-
this.almostEqualsNumber(vec1.y, vec2.y, acceptableDifference));
|
|
3023
|
+
static almostEquals(value1, value2, acceptableDifference = this.FLOAT_EPSILON) {
|
|
3024
|
+
if (value1 instanceof Vector2 && value2 instanceof Vector2) {
|
|
3025
|
+
return (this.almostEquals(value1.x, value2.x, acceptableDifference) &&
|
|
3026
|
+
this.almostEquals(value1.y, value2.y, acceptableDifference));
|
|
3027
|
+
}
|
|
3028
|
+
else {
|
|
3029
|
+
return (Math.abs(value1 - value2) <=
|
|
3030
|
+
acceptableDifference);
|
|
3031
|
+
}
|
|
3015
3032
|
}
|
|
3016
3033
|
/**
|
|
3017
3034
|
* Checks whether two real numbers are almost equal.
|
|
@@ -3161,7 +3178,7 @@ class PathApproximator {
|
|
|
3161
3178
|
const c = controlPoints[2];
|
|
3162
3179
|
// If we have a degenerate triangle where a side-length is almost zero, then give up and fall
|
|
3163
3180
|
// back to a more numerically stable method.
|
|
3164
|
-
if (Precision.
|
|
3181
|
+
if (Precision.almostEquals(0, (b.y - a.y) * (c.x - a.x) - (b.x - a.x) * (c.y - a.y))) {
|
|
3165
3182
|
return this.approximateBezier(controlPoints);
|
|
3166
3183
|
}
|
|
3167
3184
|
// See: https://en.wikipedia.org/wiki/Circumscribed_circle#Cartesian_coordinates_2
|
|
@@ -3516,7 +3533,7 @@ class SliderPath {
|
|
|
3516
3533
|
const d0 = this.cumulativeLength[i - 1];
|
|
3517
3534
|
const d1 = this.cumulativeLength[i];
|
|
3518
3535
|
// Avoid division by and almost-zero number in case two points are extremely close to each other.
|
|
3519
|
-
if (Precision.
|
|
3536
|
+
if (Precision.almostEquals(d0, d1)) {
|
|
3520
3537
|
return p0;
|
|
3521
3538
|
}
|
|
3522
3539
|
const w = (d - d0) / (d1 - d0);
|
|
@@ -3758,7 +3775,7 @@ class HitObjectGenerationUtils {
|
|
|
3758
3775
|
/**
|
|
3759
3776
|
* Determines whether a {@link HitObject} is on a beat.
|
|
3760
3777
|
*
|
|
3761
|
-
* @param beatmap The {@link
|
|
3778
|
+
* @param beatmap The {@link IBeatmap} the {@link HitObject} is a part of.
|
|
3762
3779
|
* @param hitObject The {@link HitObject} to check.
|
|
3763
3780
|
* @param downbeatsOnly If `true`, whether this method only returns `true` is on a downbeat.
|
|
3764
3781
|
* @return `true` if the {@link HitObject} is on a (down-)beat, `false` otherwise.
|
|
@@ -3866,7 +3883,7 @@ class HitObjectGenerationUtils {
|
|
|
3866
3883
|
this.getSliderRotation(current.hitObject)));
|
|
3867
3884
|
const relativeRotation = Math.atan2(centerOfMassModified.y, centerOfMassModified.x) -
|
|
3868
3885
|
Math.atan2(centerOfMassOriginal.y, centerOfMassOriginal.x);
|
|
3869
|
-
if (!Precision.
|
|
3886
|
+
if (!Precision.almostEquals(relativeRotation, 0)) {
|
|
3870
3887
|
this.rotateSlider(current.hitObject, relativeRotation);
|
|
3871
3888
|
}
|
|
3872
3889
|
}
|
|
@@ -4327,7 +4344,7 @@ class NullableDecimalModSetting extends RangeConstrainedModSetting {
|
|
|
4327
4344
|
}
|
|
4328
4345
|
set precision(value) {
|
|
4329
4346
|
if (value !== null && value < 0) {
|
|
4330
|
-
throw new RangeError(`The precision (${value}) must be greater than or equal to 0.`);
|
|
4347
|
+
throw new RangeError(`The precision (${value.toString()}) must be greater than or equal to 0.`);
|
|
4331
4348
|
}
|
|
4332
4349
|
this._precision = value;
|
|
4333
4350
|
if (value !== null) {
|
|
@@ -4346,14 +4363,14 @@ class NullableDecimalModSetting extends RangeConstrainedModSetting {
|
|
|
4346
4363
|
return super.toDisplayString();
|
|
4347
4364
|
};
|
|
4348
4365
|
if (min > max) {
|
|
4349
|
-
throw new RangeError(`The minimum value (${min}) must be less than or equal to the maximum value (${max}).`);
|
|
4366
|
+
throw new RangeError(`The minimum value (${min.toString()}) must be less than or equal to the maximum value (${max.toString()}).`);
|
|
4350
4367
|
}
|
|
4351
4368
|
if (step < 0) {
|
|
4352
|
-
throw new RangeError(`The step size (${step}) must be greater than or equal to 0.`);
|
|
4369
|
+
throw new RangeError(`The step size (${step.toString()}) must be greater than or equal to 0.`);
|
|
4353
4370
|
}
|
|
4354
4371
|
if (defaultValue !== null &&
|
|
4355
4372
|
(defaultValue < min || defaultValue > max)) {
|
|
4356
|
-
throw new RangeError(`The default value (${defaultValue}) must be between the minimum (${min}) and maximum (${max}) values.`);
|
|
4373
|
+
throw new RangeError(`The default value (${defaultValue.toString()}) must be between the minimum (${min.toString()}) and maximum (${max.toString()}) values.`);
|
|
4357
4374
|
}
|
|
4358
4375
|
this._precision = precision;
|
|
4359
4376
|
}
|
|
@@ -4943,11 +4960,11 @@ class NullableIntegerModSetting extends RangeConstrainedModSetting {
|
|
|
4943
4960
|
super(name, description, defaultValue, min, max, 1);
|
|
4944
4961
|
this.displayFormatter = (v) => { var _a; return (_a = v === null || v === void 0 ? void 0 : v.toString()) !== null && _a !== void 0 ? _a : "None"; };
|
|
4945
4962
|
if (min > max) {
|
|
4946
|
-
throw new RangeError(`The minimum value (${min}) must be less than or equal to the maximum value (${max}).`);
|
|
4963
|
+
throw new RangeError(`The minimum value (${min.toString()}) must be less than or equal to the maximum value (${max.toString()}).`);
|
|
4947
4964
|
}
|
|
4948
4965
|
if (defaultValue !== null &&
|
|
4949
4966
|
(defaultValue < min || defaultValue > max)) {
|
|
4950
|
-
throw new RangeError(`The default value (${defaultValue}) must be between the minimum (${min}) and maximum (${max}) values.`);
|
|
4967
|
+
throw new RangeError(`The default value (${defaultValue.toString()}) must be between the minimum (${min.toString()}) and maximum (${max.toString()}) values.`);
|
|
4951
4968
|
}
|
|
4952
4969
|
}
|
|
4953
4970
|
processValue(value) {
|
|
@@ -5107,7 +5124,7 @@ class ModRandom extends Mod {
|
|
|
5107
5124
|
toString() {
|
|
5108
5125
|
const settings = [];
|
|
5109
5126
|
if (this.seed.value !== null) {
|
|
5110
|
-
settings.push(`seed: ${this.seed.value}`);
|
|
5127
|
+
settings.push(`seed: ${this.seed.value.toString()}`);
|
|
5111
5128
|
}
|
|
5112
5129
|
settings.push(`angle sharpness: ${this.angleSharpness.toDisplayString()}`);
|
|
5113
5130
|
return `${super.toString()} (${settings.join(", ")})`;
|
|
@@ -5143,12 +5160,8 @@ class ModSpunOut extends Mod {
|
|
|
5143
5160
|
this.name = "SpunOut";
|
|
5144
5161
|
this.osuRanked = true;
|
|
5145
5162
|
this.bitwise = 1 << 12;
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
return true;
|
|
5149
|
-
}
|
|
5150
|
-
get osuScoreMultiplier() {
|
|
5151
|
-
return 0.9;
|
|
5163
|
+
this.isOsuRelevant = true;
|
|
5164
|
+
this.osuScoreMultiplier = 0.9;
|
|
5152
5165
|
}
|
|
5153
5166
|
}
|
|
5154
5167
|
|
|
@@ -5179,12 +5192,8 @@ class ModTouchDevice extends Mod {
|
|
|
5179
5192
|
this.name = "TouchDevice";
|
|
5180
5193
|
this.osuRanked = true;
|
|
5181
5194
|
this.bitwise = 1 << 2;
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
return true;
|
|
5185
|
-
}
|
|
5186
|
-
get osuScoreMultiplier() {
|
|
5187
|
-
return 1;
|
|
5195
|
+
this.isOsuRelevant = true;
|
|
5196
|
+
this.osuScoreMultiplier = 1;
|
|
5188
5197
|
}
|
|
5189
5198
|
}
|
|
5190
5199
|
|
|
@@ -6144,8 +6153,7 @@ class BeatmapProcessor {
|
|
|
6144
6153
|
if (objectN instanceof Spinner) {
|
|
6145
6154
|
break;
|
|
6146
6155
|
}
|
|
6147
|
-
const stackThreshold = objectN
|
|
6148
|
-
this.beatmap.general.stackLeniency;
|
|
6156
|
+
const stackThreshold = this.calculateStackThreshold(objectN);
|
|
6149
6157
|
if (objectN.startTime - stackBaseObject.endTime >
|
|
6150
6158
|
stackThreshold) {
|
|
6151
6159
|
// We are no longer within stacking range of the next object.
|
|
@@ -6187,7 +6195,7 @@ class BeatmapProcessor {
|
|
|
6187
6195
|
if (objectI.stackHeight !== 0 || objectI instanceof Spinner) {
|
|
6188
6196
|
continue;
|
|
6189
6197
|
}
|
|
6190
|
-
const stackThreshold =
|
|
6198
|
+
const stackThreshold = this.calculateStackThreshold(objectI);
|
|
6191
6199
|
// If this object is a hit circle, then we enter this "special" case.
|
|
6192
6200
|
// It either ends with a stack of hit circles only, or a stack of hit circles that are underneath a slider.
|
|
6193
6201
|
// Any other case is handled by the "instanceof Slider" code below this.
|
|
@@ -6197,7 +6205,11 @@ class BeatmapProcessor {
|
|
|
6197
6205
|
if (objectN instanceof Spinner) {
|
|
6198
6206
|
continue;
|
|
6199
6207
|
}
|
|
6200
|
-
|
|
6208
|
+
// Truncation to integer is required to match osu!stable - both quantities being subtracted there
|
|
6209
|
+
// are integers.
|
|
6210
|
+
if (Math.trunc(objectI.startTime) -
|
|
6211
|
+
Math.trunc(objectN.endTime) >
|
|
6212
|
+
stackThreshold) {
|
|
6201
6213
|
// We are no longer within stacking range of the previous object.
|
|
6202
6214
|
break;
|
|
6203
6215
|
}
|
|
@@ -6266,7 +6278,7 @@ class BeatmapProcessor {
|
|
|
6266
6278
|
}
|
|
6267
6279
|
let startTime = currentObject.endTime;
|
|
6268
6280
|
let sliderStack = 0;
|
|
6269
|
-
const stackThreshold =
|
|
6281
|
+
const stackThreshold = this.calculateStackThreshold(currentObject);
|
|
6270
6282
|
for (let j = i + 1; j < objects.length; ++j) {
|
|
6271
6283
|
if (objects[j].startTime - stackThreshold > startTime) {
|
|
6272
6284
|
break;
|
|
@@ -6294,6 +6306,18 @@ class BeatmapProcessor {
|
|
|
6294
6306
|
}
|
|
6295
6307
|
}
|
|
6296
6308
|
}
|
|
6309
|
+
/**
|
|
6310
|
+
* Truncation of {@link HitObject.timePreempt} to an integer, as well as keeping the result as a float,
|
|
6311
|
+
* are both done for the purposes of osu!stable compatibility.
|
|
6312
|
+
*
|
|
6313
|
+
* Note that top-level objects {@link HitObject.timePreempt} is supposed to be integral anyway; see
|
|
6314
|
+
* {@link HitObject.applyDefaults} using `BeatmapDifficulty.difficultyRangeInt` when calculating it.
|
|
6315
|
+
*
|
|
6316
|
+
* Slider ticks and end circles are the exception to that, but they do not matter for stacking.
|
|
6317
|
+
*/
|
|
6318
|
+
calculateStackThreshold(object) {
|
|
6319
|
+
return (Math.trunc(object.timePreempt) * this.beatmap.general.stackLeniency);
|
|
6320
|
+
}
|
|
6297
6321
|
}
|
|
6298
6322
|
BeatmapProcessor.stackDistance = 3;
|
|
6299
6323
|
|
|
@@ -6393,13 +6417,13 @@ class TimingControlPoint extends ControlPoint {
|
|
|
6393
6417
|
}
|
|
6394
6418
|
toString() {
|
|
6395
6419
|
return ("{ time: " +
|
|
6396
|
-
this.time +
|
|
6420
|
+
this.time.toString() +
|
|
6397
6421
|
", " +
|
|
6398
6422
|
"ms_per_beat: " +
|
|
6399
6423
|
this.msPerBeat.toFixed(2) +
|
|
6400
6424
|
", " +
|
|
6401
6425
|
"timeSignature: " +
|
|
6402
|
-
this.timeSignature +
|
|
6426
|
+
this.timeSignature.toString() +
|
|
6403
6427
|
" }");
|
|
6404
6428
|
}
|
|
6405
6429
|
}
|
|
@@ -6611,12 +6635,12 @@ class DifficultyControlPoint extends ControlPoint {
|
|
|
6611
6635
|
}
|
|
6612
6636
|
toString() {
|
|
6613
6637
|
return ("{ time: " +
|
|
6614
|
-
this.time +
|
|
6638
|
+
this.time.toString() +
|
|
6615
6639
|
", " +
|
|
6616
6640
|
"speed multiplier: " +
|
|
6617
6641
|
this.speedMultiplier.toFixed(2) +
|
|
6618
6642
|
", generate ticks: " +
|
|
6619
|
-
this.generateTicks +
|
|
6643
|
+
this.generateTicks.toString() +
|
|
6620
6644
|
" }");
|
|
6621
6645
|
}
|
|
6622
6646
|
}
|
|
@@ -6651,7 +6675,12 @@ class EffectControlPoint extends ControlPoint {
|
|
|
6651
6675
|
return this.isKiai === existing.isKiai;
|
|
6652
6676
|
}
|
|
6653
6677
|
toString() {
|
|
6654
|
-
return "{ time: " +
|
|
6678
|
+
return ("{ time: " +
|
|
6679
|
+
this.time.toString() +
|
|
6680
|
+
", " +
|
|
6681
|
+
"kiai: " +
|
|
6682
|
+
this.isKiai.toString() +
|
|
6683
|
+
" }");
|
|
6655
6684
|
}
|
|
6656
6685
|
}
|
|
6657
6686
|
|
|
@@ -6712,13 +6741,13 @@ class SampleControlPoint extends ControlPoint {
|
|
|
6712
6741
|
}
|
|
6713
6742
|
toString() {
|
|
6714
6743
|
return ("{ time: " +
|
|
6715
|
-
this.time +
|
|
6744
|
+
this.time.toString() +
|
|
6716
6745
|
", " +
|
|
6717
6746
|
"sample bank: " +
|
|
6718
|
-
this.sampleBank +
|
|
6747
|
+
this.sampleBank.toString() +
|
|
6719
6748
|
", " +
|
|
6720
6749
|
"sample volume: " +
|
|
6721
|
-
this.sampleVolume +
|
|
6750
|
+
this.sampleVolume.toString() +
|
|
6722
6751
|
" }");
|
|
6723
6752
|
}
|
|
6724
6753
|
}
|
|
@@ -6774,6 +6803,7 @@ class BeatmapControlPoints {
|
|
|
6774
6803
|
}
|
|
6775
6804
|
}
|
|
6776
6805
|
|
|
6806
|
+
/* eslint-disable @typescript-eslint/prefer-literal-enum-member */
|
|
6777
6807
|
/**
|
|
6778
6808
|
* Represents the grid size setting in the editor.
|
|
6779
6809
|
*/
|
|
@@ -7060,7 +7090,7 @@ class Beatmap {
|
|
|
7060
7090
|
var _a, _b, _c, _d, _e;
|
|
7061
7091
|
// The last playable time in the beatmap - the last timing point extends to this time.
|
|
7062
7092
|
// Note: This is more accurate and may present different results because osu-stable didn't have the ability to calculate slider durations in this context.
|
|
7063
|
-
const lastTime = (_d = (_b = (_a = this.hitObjects.objects
|
|
7093
|
+
const lastTime = (_d = (_b = (_a = this.hitObjects.objects.at(-1)) === null || _a === void 0 ? void 0 : _a.endTime) !== null && _b !== void 0 ? _b : (_c = this.controlPoints.timing.points.at(-1)) === null || _c === void 0 ? void 0 : _c.time) !== null && _d !== void 0 ? _d : 0;
|
|
7064
7094
|
const mostCommon =
|
|
7065
7095
|
// Construct a set of {beatLength, duration} objects for each individual timing point.
|
|
7066
7096
|
this.controlPoints.timing.points
|
|
@@ -7076,8 +7106,9 @@ class Beatmap {
|
|
|
7076
7106
|
duration: nextTime - currentTime,
|
|
7077
7107
|
};
|
|
7078
7108
|
})
|
|
7079
|
-
|
|
7080
|
-
.
|
|
7109
|
+
.sort((a, b) => b.duration - a.duration)
|
|
7110
|
+
.at(0);
|
|
7111
|
+
// Get the most common one, or 0 as a suitable default.
|
|
7081
7112
|
return (_e = mostCommon === null || mostCommon === void 0 ? void 0 : mostCommon.beatLength) !== null && _e !== void 0 ? _e : 0;
|
|
7082
7113
|
}
|
|
7083
7114
|
getOffsetTime(time) {
|
|
@@ -7232,7 +7263,9 @@ class Beatmap {
|
|
|
7232
7263
|
const processor = new BeatmapProcessor(converted);
|
|
7233
7264
|
processor.preProcess();
|
|
7234
7265
|
// Compute default values for hit objects, including creating nested hit objects in-case they're needed.
|
|
7235
|
-
converted.hitObjects.objects.forEach((hitObject) =>
|
|
7266
|
+
converted.hitObjects.objects.forEach((hitObject) => {
|
|
7267
|
+
hitObject.applyDefaults(converted.controlPoints, converted.difficulty, mode);
|
|
7268
|
+
});
|
|
7236
7269
|
mods.forEach((mod) => {
|
|
7237
7270
|
if (mod.isApplicableToHitObject()) {
|
|
7238
7271
|
for (const hitObject of converted.hitObjects.objects) {
|
|
@@ -7275,25 +7308,25 @@ class Beatmap {
|
|
|
7275
7308
|
"\n" +
|
|
7276
7309
|
"\n" +
|
|
7277
7310
|
"AR" +
|
|
7278
|
-
MathUtils.round(this.difficulty.ar, 2) +
|
|
7311
|
+
MathUtils.round(this.difficulty.ar, 2).toString() +
|
|
7279
7312
|
" " +
|
|
7280
7313
|
"OD" +
|
|
7281
|
-
MathUtils.round(this.difficulty.od, 2) +
|
|
7314
|
+
MathUtils.round(this.difficulty.od, 2).toString() +
|
|
7282
7315
|
" " +
|
|
7283
7316
|
"CS" +
|
|
7284
|
-
MathUtils.round(this.difficulty.cs, 2) +
|
|
7317
|
+
MathUtils.round(this.difficulty.cs, 2).toString() +
|
|
7285
7318
|
" " +
|
|
7286
7319
|
"HP" +
|
|
7287
|
-
MathUtils.round(this.difficulty.hp, 2) +
|
|
7320
|
+
MathUtils.round(this.difficulty.hp, 2).toString() +
|
|
7288
7321
|
"\n" +
|
|
7289
|
-
this.hitObjects.circles +
|
|
7322
|
+
this.hitObjects.circles.toString() +
|
|
7290
7323
|
" circles, " +
|
|
7291
|
-
this.hitObjects.sliders +
|
|
7324
|
+
this.hitObjects.sliders.toString() +
|
|
7292
7325
|
" sliders, " +
|
|
7293
|
-
this.hitObjects.spinners +
|
|
7326
|
+
this.hitObjects.spinners.toString() +
|
|
7294
7327
|
" spinners" +
|
|
7295
7328
|
"\n" +
|
|
7296
|
-
this.maxCombo +
|
|
7329
|
+
this.maxCombo.toString() +
|
|
7297
7330
|
" max combo";
|
|
7298
7331
|
return res;
|
|
7299
7332
|
}
|
|
@@ -7309,6 +7342,7 @@ class BeatmapBackground {
|
|
|
7309
7342
|
}
|
|
7310
7343
|
}
|
|
7311
7344
|
|
|
7345
|
+
/* eslint-disable @typescript-eslint/prefer-literal-enum-member */
|
|
7312
7346
|
/**
|
|
7313
7347
|
* Represents available hitsound types.
|
|
7314
7348
|
*/
|
|
@@ -7321,6 +7355,7 @@ exports.HitSoundType = void 0;
|
|
|
7321
7355
|
HitSoundType[HitSoundType["clap"] = 8] = "clap";
|
|
7322
7356
|
})(exports.HitSoundType || (exports.HitSoundType = {}));
|
|
7323
7357
|
|
|
7358
|
+
/* eslint-disable @typescript-eslint/no-duplicate-enum-values */
|
|
7324
7359
|
/**
|
|
7325
7360
|
* Constants for beatmap parser.
|
|
7326
7361
|
*/
|
|
@@ -7403,7 +7438,7 @@ class Decoder {
|
|
|
7403
7438
|
* @returns The current decoder instance.
|
|
7404
7439
|
*/
|
|
7405
7440
|
decode(str) {
|
|
7406
|
-
var _a;
|
|
7441
|
+
var _a, _b;
|
|
7407
7442
|
this.reset();
|
|
7408
7443
|
for (let line of str.split("\n")) {
|
|
7409
7444
|
this.currentLine = line;
|
|
@@ -7424,7 +7459,7 @@ class Decoder {
|
|
|
7424
7459
|
if (line.startsWith("[") && line.endsWith("]")) {
|
|
7425
7460
|
const section = line.substring(1, line.length - 1);
|
|
7426
7461
|
if (!Object.values(BeatmapSection).includes(section)) {
|
|
7427
|
-
console.warn(`Unknown section "${line}" at line ${this.line}`);
|
|
7462
|
+
console.warn(`Unknown section "${line}" at line ${this.line.toString()}`);
|
|
7428
7463
|
continue;
|
|
7429
7464
|
}
|
|
7430
7465
|
this.section = section;
|
|
@@ -7443,7 +7478,7 @@ class Decoder {
|
|
|
7443
7478
|
}
|
|
7444
7479
|
catch (e) {
|
|
7445
7480
|
console.error(e);
|
|
7446
|
-
console.error(`at line ${this.line}\n${(_a = this.decoders[this.section]) === null || _a === void 0 ? void 0 : _a.logExceptionPosition()}`);
|
|
7481
|
+
console.error(`at line ${this.line.toString()}\n${(_b = (_a = this.decoders[this.section]) === null || _a === void 0 ? void 0 : _a.logExceptionPosition()) !== null && _b !== void 0 ? _b : ""}`);
|
|
7447
7482
|
}
|
|
7448
7483
|
}
|
|
7449
7484
|
return this;
|
|
@@ -7533,7 +7568,7 @@ class SectionDecoder {
|
|
|
7533
7568
|
* @param str The string to parse.
|
|
7534
7569
|
* @param min The minimum threshold. Defaults to `-ParserConstants.MAX_PARSE_VALUE`.
|
|
7535
7570
|
* @param max The maximum threshold. Defaults to `ParserConstants.MAX_PARSE_VALUE`.
|
|
7536
|
-
* @param allowNaN Whether to allow NaN.
|
|
7571
|
+
* @param allowNaN Whether to allow NaN. Defaults to `false`.
|
|
7537
7572
|
* @returns The parsed integer.
|
|
7538
7573
|
*/
|
|
7539
7574
|
tryParseInt(str, min = -ParserConstants.MAX_PARSE_VALUE, max = ParserConstants.MAX_PARSE_VALUE, allowNaN = false) {
|
|
@@ -7557,7 +7592,7 @@ class SectionDecoder {
|
|
|
7557
7592
|
* @param str The string to parse.
|
|
7558
7593
|
* @param min The minimum threshold. Defaults to `-ParserConstants.MAX_PARSE_VALUE`.
|
|
7559
7594
|
* @param max The maximum threshold. Defaults to `ParserConstants.MAX_PARSE_VALUE`.
|
|
7560
|
-
* @param allowNaN Whether to allow NaN.
|
|
7595
|
+
* @param allowNaN Whether to allow NaN. Defaults to `false`.
|
|
7561
7596
|
* @returns The parsed float.
|
|
7562
7597
|
*/
|
|
7563
7598
|
tryParseFloat(str, min = -ParserConstants.MAX_PARSE_VALUE, max = ParserConstants.MAX_PARSE_VALUE, allowNaN = false) {
|
|
@@ -7662,7 +7697,7 @@ class BeatmapHitObjectsDecoder extends SectionDecoder {
|
|
|
7662
7697
|
if (points.length !== 3) {
|
|
7663
7698
|
pathType = exports.PathType.Bezier;
|
|
7664
7699
|
}
|
|
7665
|
-
else if (Precision.
|
|
7700
|
+
else if (Precision.almostEquals(0, (points[1].y - points[0].y) *
|
|
7666
7701
|
(points[2].x - points[0].x) -
|
|
7667
7702
|
(points[1].x - points[0].x) *
|
|
7668
7703
|
(points[2].y - points[0].y))) {
|
|
@@ -7835,7 +7870,7 @@ class BeatmapGeneralDecoder extends SectionDecoder {
|
|
|
7835
7870
|
this.target.general.previewTime = this.tryParseInt(p[1]);
|
|
7836
7871
|
break;
|
|
7837
7872
|
case "Countdown":
|
|
7838
|
-
this.target.general.countdown =
|
|
7873
|
+
this.target.general.countdown = this.tryParseInt(p[1]);
|
|
7839
7874
|
break;
|
|
7840
7875
|
case "SampleSet":
|
|
7841
7876
|
switch (p[1]) {
|
|
@@ -7866,7 +7901,8 @@ class BeatmapGeneralDecoder extends SectionDecoder {
|
|
|
7866
7901
|
this.target.general.useSkinSprites = !!this.tryParseInt(p[1]);
|
|
7867
7902
|
break;
|
|
7868
7903
|
case "OverlayPosition":
|
|
7869
|
-
this.target.general.overlayPosition =
|
|
7904
|
+
this.target.general.overlayPosition =
|
|
7905
|
+
p[1];
|
|
7870
7906
|
break;
|
|
7871
7907
|
case "SkinPreference":
|
|
7872
7908
|
this.target.general.skinPreference = (_a = p[1]) !== null && _a !== void 0 ? _a : "";
|
|
@@ -7889,6 +7925,7 @@ class BeatmapGeneralDecoder extends SectionDecoder {
|
|
|
7889
7925
|
this.target.editor.bookmarks = p[1]
|
|
7890
7926
|
.split(",")
|
|
7891
7927
|
.map((v) => this.tryParseInt(v));
|
|
7928
|
+
break;
|
|
7892
7929
|
}
|
|
7893
7930
|
}
|
|
7894
7931
|
}
|
|
@@ -7912,7 +7949,7 @@ class BeatmapEditorDecoder extends SectionDecoder {
|
|
|
7912
7949
|
this.target.editor.beatDivisor = this.tryParseFloat(p[1]);
|
|
7913
7950
|
break;
|
|
7914
7951
|
case "GridSize":
|
|
7915
|
-
this.target.editor.gridSize =
|
|
7952
|
+
this.target.editor.gridSize = this.tryParseInt(p[1]);
|
|
7916
7953
|
break;
|
|
7917
7954
|
case "TimelineZoom":
|
|
7918
7955
|
this.target.editor.timelineZoom = this.tryParseFloat(p[1]);
|
|
@@ -7942,15 +7979,15 @@ class BreakPoint {
|
|
|
7942
7979
|
get duration() {
|
|
7943
7980
|
return this.endTime - this.startTime;
|
|
7944
7981
|
}
|
|
7945
|
-
constructor(
|
|
7946
|
-
this.startTime =
|
|
7947
|
-
this.endTime =
|
|
7982
|
+
constructor(startTime, endTime) {
|
|
7983
|
+
this.startTime = startTime;
|
|
7984
|
+
this.endTime = endTime;
|
|
7948
7985
|
}
|
|
7949
7986
|
/**
|
|
7950
7987
|
* Returns a string representation of the class.
|
|
7951
7988
|
*/
|
|
7952
7989
|
toString() {
|
|
7953
|
-
return `Start time: ${this.startTime}, end time: ${this.endTime}, duration: ${this.duration}`;
|
|
7990
|
+
return `Start time: ${this.startTime.toString()}, end time: ${this.endTime.toString()}, duration: ${this.duration.toString()}`;
|
|
7954
7991
|
}
|
|
7955
7992
|
/**
|
|
7956
7993
|
* Whether this break period contains a specified time.
|
|
@@ -8003,10 +8040,7 @@ class BeatmapEventsDecoder extends SectionDecoder {
|
|
|
8003
8040
|
this.target.events.video = new BeatmapVideo(this.tryParseInt(this.setPosition(s[1])), this.setPosition(s[2]).replace(/"/g, ""), new Vector2(this.tryParseFloat(this.setPosition((_a = s[3]) !== null && _a !== void 0 ? _a : "0")), this.tryParseFloat(this.setPosition((_b = s[4]) !== null && _b !== void 0 ? _b : "0"))));
|
|
8004
8041
|
}
|
|
8005
8042
|
parseBreak(s) {
|
|
8006
|
-
this.target.events.breaks.push(new BreakPoint(
|
|
8007
|
-
startTime: this.target.getOffsetTime(this.tryParseInt(this.setPosition(s[1]))),
|
|
8008
|
-
endTime: this.target.getOffsetTime(this.tryParseInt(this.setPosition(s[2]))),
|
|
8009
|
-
}));
|
|
8043
|
+
this.target.events.breaks.push(new BreakPoint(this.target.getOffsetTime(this.tryParseInt(this.setPosition(s[1]))), this.target.getOffsetTime(this.tryParseInt(this.setPosition(s[2])))));
|
|
8010
8044
|
}
|
|
8011
8045
|
}
|
|
8012
8046
|
|
|
@@ -8079,6 +8113,7 @@ class BeatmapMetadataDecoder extends SectionDecoder {
|
|
|
8079
8113
|
}
|
|
8080
8114
|
}
|
|
8081
8115
|
|
|
8116
|
+
/* eslint-disable @typescript-eslint/prefer-literal-enum-member */
|
|
8082
8117
|
/**
|
|
8083
8118
|
* Effects that can occur in an effect control point.
|
|
8084
8119
|
*/
|
|
@@ -8177,10 +8212,10 @@ class RGBColor {
|
|
|
8177
8212
|
*/
|
|
8178
8213
|
toString() {
|
|
8179
8214
|
if (this.a === 1) {
|
|
8180
|
-
return `${this.r},${this.g},${this.b}`;
|
|
8215
|
+
return `${this.r.toString()},${this.g.toString()},${this.b.toString()}`;
|
|
8181
8216
|
}
|
|
8182
8217
|
else {
|
|
8183
|
-
return `${this.r},${this.g},${this.b},${this.a}`;
|
|
8218
|
+
return `${this.r.toString()},${this.g.toString()},${this.b.toString()},${this.a.toString()}`;
|
|
8184
8219
|
}
|
|
8185
8220
|
}
|
|
8186
8221
|
/**
|
|
@@ -8462,7 +8497,8 @@ class Command {
|
|
|
8462
8497
|
return this.parameterType !== undefined;
|
|
8463
8498
|
}
|
|
8464
8499
|
toString() {
|
|
8465
|
-
|
|
8500
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
8501
|
+
return `${this.startTime.toString()} -> ${this.endTime.toString()}, ${this.startValue} -> ${this.endValue} ${this.easing.toString()}`;
|
|
8466
8502
|
}
|
|
8467
8503
|
}
|
|
8468
8504
|
|
|
@@ -8684,7 +8720,7 @@ class CommandLoop extends CommandTimelineGroup {
|
|
|
8684
8720
|
return commands;
|
|
8685
8721
|
}
|
|
8686
8722
|
toString() {
|
|
8687
|
-
return `${this.loopStartTime} x${this.totalIterations}`;
|
|
8723
|
+
return `${this.loopStartTime.toString()} x${this.totalIterations.toString()}`;
|
|
8688
8724
|
}
|
|
8689
8725
|
}
|
|
8690
8726
|
|
|
@@ -8700,7 +8736,7 @@ class CommandTrigger extends CommandTimelineGroup {
|
|
|
8700
8736
|
this.groupNumber = groupNumber;
|
|
8701
8737
|
}
|
|
8702
8738
|
toString() {
|
|
8703
|
-
return `${this.triggerName} ${this.triggerStartTime} -> ${this.triggerEndTime} (${this.groupNumber})`;
|
|
8739
|
+
return `${this.triggerName} ${this.triggerStartTime.toString()} -> ${this.triggerEndTime.toString()} (${this.groupNumber.toString()})`;
|
|
8704
8740
|
}
|
|
8705
8741
|
}
|
|
8706
8742
|
|
|
@@ -8736,7 +8772,7 @@ class StoryboardSprite extends StoryboardElement {
|
|
|
8736
8772
|
// You can imagine that the first command of each type decides that type's start value, so if the initial alpha is zero,
|
|
8737
8773
|
// anything before that point can be ignored (the sprite is not visible after all).
|
|
8738
8774
|
const alphaCommands = [];
|
|
8739
|
-
let command = this.timelineGroup.alpha.commands
|
|
8775
|
+
let command = this.timelineGroup.alpha.commands.at(0);
|
|
8740
8776
|
if (command) {
|
|
8741
8777
|
alphaCommands.push({
|
|
8742
8778
|
startTime: command.startTime,
|
|
@@ -8744,7 +8780,7 @@ class StoryboardSprite extends StoryboardElement {
|
|
|
8744
8780
|
});
|
|
8745
8781
|
}
|
|
8746
8782
|
for (const l of this.loops) {
|
|
8747
|
-
command = l.alpha.commands
|
|
8783
|
+
command = l.alpha.commands.at(0);
|
|
8748
8784
|
if (command) {
|
|
8749
8785
|
alphaCommands.push({
|
|
8750
8786
|
startTime: command.startTime + l.loopStartTime,
|
|
@@ -8826,7 +8862,7 @@ class StoryboardSprite extends StoryboardElement {
|
|
|
8826
8862
|
return trigger;
|
|
8827
8863
|
}
|
|
8828
8864
|
toString() {
|
|
8829
|
-
return `${this.path}, ${this.origin}, ${this.initialPosition}`;
|
|
8865
|
+
return `${this.path}, ${this.origin}, ${this.initialPosition.toString()}`;
|
|
8830
8866
|
}
|
|
8831
8867
|
}
|
|
8832
8868
|
|
|
@@ -8963,7 +8999,7 @@ class StoryboardEventsDecoder extends SectionDecoder {
|
|
|
8963
8999
|
if (!s[3]) {
|
|
8964
9000
|
s[3] = this.setPosition(s[2]);
|
|
8965
9001
|
}
|
|
8966
|
-
const easing =
|
|
9002
|
+
const easing = this.tryParseInt(this.setPosition(s[1]));
|
|
8967
9003
|
const startTime = this.tryParseInt(this.setPosition(s[2]));
|
|
8968
9004
|
const endTime = this.tryParseInt(this.setPosition(s[3]));
|
|
8969
9005
|
switch (s[0]) {
|
|
@@ -9221,13 +9257,13 @@ class BeatmapDecoder extends Decoder {
|
|
|
9221
9257
|
/**
|
|
9222
9258
|
* @param str The string to decode.
|
|
9223
9259
|
* @param mode The mode to parse the beatmap as. Defaults to osu!standard.
|
|
9224
|
-
* @param parseStoryboard Whether to parse the beatmap's storyboard.
|
|
9260
|
+
* @param parseStoryboard Whether to parse the beatmap's storyboard. Defaults to `true`.
|
|
9225
9261
|
*/
|
|
9226
9262
|
decode(str, mode = exports.Modes.osu, parseStoryboard = true) {
|
|
9227
9263
|
super.decode(str);
|
|
9228
9264
|
this.finalResult.mode = mode;
|
|
9229
9265
|
if (parseStoryboard) {
|
|
9230
|
-
const eventsDecoder =
|
|
9266
|
+
const eventsDecoder = this.decoders[BeatmapSection.events];
|
|
9231
9267
|
if (eventsDecoder.storyboardLines.length > 0) {
|
|
9232
9268
|
this.finalResult.events.storyboard = new StoryboardDecoder(this.finalResult.formatVersion).decode(eventsDecoder.storyboardLines.join("\n")).result;
|
|
9233
9269
|
}
|
|
@@ -9315,6 +9351,9 @@ class Encoder {
|
|
|
9315
9351
|
* The base of all encoders.
|
|
9316
9352
|
*/
|
|
9317
9353
|
class BaseEncoder {
|
|
9354
|
+
/**
|
|
9355
|
+
* @param encodeSections Whether sections should be encoded. Defaults to `true`.
|
|
9356
|
+
*/
|
|
9318
9357
|
constructor(encodeSections = true) {
|
|
9319
9358
|
/**
|
|
9320
9359
|
* The target of the encoding process.
|
|
@@ -9393,10 +9432,10 @@ class BeatmapColorEncoder extends BeatmapBaseEncoder {
|
|
|
9393
9432
|
}
|
|
9394
9433
|
for (let i = 0; i < colors.combo.length; ++i) {
|
|
9395
9434
|
const color = colors.combo[i];
|
|
9396
|
-
this.write(`Combo${i + 1}: `);
|
|
9397
|
-
this.write(`${color.r},`);
|
|
9398
|
-
this.write(`${color.g},`);
|
|
9399
|
-
this.write(
|
|
9435
|
+
this.write(`Combo${(i + 1).toString()}: `);
|
|
9436
|
+
this.write(`${color.r.toString()},`);
|
|
9437
|
+
this.write(`${color.g.toString()},`);
|
|
9438
|
+
this.write(color.b.toString());
|
|
9400
9439
|
this.writeLine();
|
|
9401
9440
|
}
|
|
9402
9441
|
}
|
|
@@ -9429,14 +9468,14 @@ class BeatmapControlPointsEncoder extends BeatmapBaseEncoder {
|
|
|
9429
9468
|
for (const group of Object.values(this.controlPointGroups).sort((a, b) => a.time - b.time)) {
|
|
9430
9469
|
// If the group contains a timing control point, it needs to be output separately.
|
|
9431
9470
|
if (group.timing) {
|
|
9432
|
-
this.write(`${group.timing.time},`);
|
|
9433
|
-
this.write(`${group.timing.msPerBeat},`);
|
|
9471
|
+
this.write(`${group.timing.time.toString()},`);
|
|
9472
|
+
this.write(`${group.timing.msPerBeat.toString()},`);
|
|
9434
9473
|
this.outputControlPointGroup(group, true);
|
|
9435
9474
|
}
|
|
9436
9475
|
// Output any remaining effects as secondary non-timing control point.
|
|
9437
|
-
this.write(`${group.time},`);
|
|
9476
|
+
this.write(`${group.time.toString()},`);
|
|
9438
9477
|
const difficultyPoint = (_a = group.difficulty) !== null && _a !== void 0 ? _a : this.map.controlPoints.difficulty.controlPointAt(group.time);
|
|
9439
|
-
this.write(`${-100 / difficultyPoint.speedMultiplier},`);
|
|
9478
|
+
this.write(`${(-100 / difficultyPoint.speedMultiplier).toString()},`);
|
|
9440
9479
|
this.outputControlPointGroup(group, false);
|
|
9441
9480
|
}
|
|
9442
9481
|
}
|
|
@@ -9484,7 +9523,7 @@ class BeatmapControlPointsEncoder extends BeatmapBaseEncoder {
|
|
|
9484
9523
|
if (effectPoint.omitFirstBarLine) {
|
|
9485
9524
|
effectFlags |= EffectFlags.omitFirstBarLine;
|
|
9486
9525
|
}
|
|
9487
|
-
this.write(`${((_c = group.timing) !== null && _c !== void 0 ? _c : this.map.controlPoints.timing.controlPointAt(group.time)).timeSignature},`);
|
|
9526
|
+
this.write(`${((_c = group.timing) !== null && _c !== void 0 ? _c : this.map.controlPoints.timing.controlPointAt(group.time)).timeSignature.toString()},`);
|
|
9488
9527
|
this.write(`${samplePoint.sampleBank.toString()},`);
|
|
9489
9528
|
this.write(`${samplePoint.customSampleBank.toString()},`);
|
|
9490
9529
|
this.write(`${samplePoint.sampleVolume.toString()},`);
|
|
@@ -9503,12 +9542,12 @@ class BeatmapDifficultyEncoder extends BeatmapBaseEncoder {
|
|
|
9503
9542
|
this.writeLine("[Difficulty]");
|
|
9504
9543
|
}
|
|
9505
9544
|
const { difficulty } = this.map;
|
|
9506
|
-
this.writeLine(`HPDrainRate: ${difficulty.hp}`);
|
|
9507
|
-
this.writeLine(`CircleSize: ${difficulty.cs}`);
|
|
9508
|
-
this.writeLine(`OverallDifficulty: ${difficulty.od}`);
|
|
9509
|
-
this.writeLine(`ApproachRate: ${difficulty.ar}`);
|
|
9510
|
-
this.writeLine(`SliderMultiplier: ${difficulty.sliderMultiplier}`);
|
|
9511
|
-
this.writeLine(`SliderTickRate: ${difficulty.sliderTickRate}`);
|
|
9545
|
+
this.writeLine(`HPDrainRate: ${difficulty.hp.toString()}`);
|
|
9546
|
+
this.writeLine(`CircleSize: ${difficulty.cs.toString()}`);
|
|
9547
|
+
this.writeLine(`OverallDifficulty: ${difficulty.od.toString()}`);
|
|
9548
|
+
this.writeLine(`ApproachRate: ${difficulty.ar.toString()}`);
|
|
9549
|
+
this.writeLine(`SliderMultiplier: ${difficulty.sliderMultiplier.toString()}`);
|
|
9550
|
+
this.writeLine(`SliderTickRate: ${difficulty.sliderTickRate.toString()}`);
|
|
9512
9551
|
}
|
|
9513
9552
|
}
|
|
9514
9553
|
|
|
@@ -9524,10 +9563,10 @@ class BeatmapEditorEncoder extends BeatmapBaseEncoder {
|
|
|
9524
9563
|
if (editor.bookmarks.length > 0) {
|
|
9525
9564
|
this.writeLine(editor.bookmarks.join());
|
|
9526
9565
|
}
|
|
9527
|
-
this.writeLine(`DistanceSpacing: ${editor.distanceSnap}`);
|
|
9528
|
-
this.writeLine(`BeatDivisor: ${editor.beatDivisor}`);
|
|
9529
|
-
this.writeLine(`GridSize: ${editor.gridSize}`);
|
|
9530
|
-
this.writeLine(`TimelineZoom: ${editor.timelineZoom}`);
|
|
9566
|
+
this.writeLine(`DistanceSpacing: ${editor.distanceSnap.toString()}`);
|
|
9567
|
+
this.writeLine(`BeatDivisor: ${editor.beatDivisor.toString()}`);
|
|
9568
|
+
this.writeLine(`GridSize: ${editor.gridSize.toString()}`);
|
|
9569
|
+
this.writeLine(`TimelineZoom: ${editor.timelineZoom.toString()}`);
|
|
9531
9570
|
}
|
|
9532
9571
|
}
|
|
9533
9572
|
|
|
@@ -9584,10 +9623,10 @@ class StoryboardEventsEncoder extends StoryboardBaseEncoder {
|
|
|
9584
9623
|
this.write(`${layerType},`);
|
|
9585
9624
|
this.write(`${element.origin},`);
|
|
9586
9625
|
this.write(`"${element.path}",`);
|
|
9587
|
-
this.write(`${element.initialPosition},`);
|
|
9588
|
-
this.write(`${element.frameCount},`);
|
|
9589
|
-
this.write(`${element.frameDelay},`);
|
|
9590
|
-
this.writeLine(
|
|
9626
|
+
this.write(`${element.initialPosition.toString()},`);
|
|
9627
|
+
this.write(`${element.frameCount.toString()},`);
|
|
9628
|
+
this.write(`${element.frameDelay.toString()},`);
|
|
9629
|
+
this.writeLine(element.loopType.toString());
|
|
9591
9630
|
this.encodeElement(element);
|
|
9592
9631
|
}
|
|
9593
9632
|
else if (element instanceof StoryboardSprite) {
|
|
@@ -9595,15 +9634,15 @@ class StoryboardEventsEncoder extends StoryboardBaseEncoder {
|
|
|
9595
9634
|
this.write(`${layerType},`);
|
|
9596
9635
|
this.write(`${element.origin},`);
|
|
9597
9636
|
this.write(`"${element.path}",`);
|
|
9598
|
-
this.writeLine(
|
|
9637
|
+
this.writeLine(element.initialPosition.toString());
|
|
9599
9638
|
this.encodeElement(element);
|
|
9600
9639
|
}
|
|
9601
9640
|
else if (element instanceof StoryboardSample) {
|
|
9602
9641
|
this.write(`${exports.StoryboardEventType.sample},`);
|
|
9603
|
-
this.write(`${element.startTime},`);
|
|
9642
|
+
this.write(`${element.startTime.toString()},`);
|
|
9604
9643
|
this.write(`${layerType},`);
|
|
9605
9644
|
this.write(`"${element.path}",`);
|
|
9606
|
-
this.writeLine(
|
|
9645
|
+
this.writeLine(element.volume.toString());
|
|
9607
9646
|
}
|
|
9608
9647
|
}
|
|
9609
9648
|
}
|
|
@@ -9620,21 +9659,21 @@ class StoryboardEventsEncoder extends StoryboardBaseEncoder {
|
|
|
9620
9659
|
if (group instanceof CommandLoop) {
|
|
9621
9660
|
this.write(" ");
|
|
9622
9661
|
this.write(`${exports.StoryboardCommandType.loop},`);
|
|
9623
|
-
this.write(`${group.startTime},`);
|
|
9624
|
-
this.write(
|
|
9662
|
+
this.write(`${group.startTime.toString()},`);
|
|
9663
|
+
this.write(group.totalIterations.toString());
|
|
9625
9664
|
}
|
|
9626
9665
|
else if (group instanceof CommandTrigger) {
|
|
9627
9666
|
this.write(" ");
|
|
9628
9667
|
this.write(`${exports.StoryboardCommandType.trigger},`);
|
|
9629
|
-
this.write(
|
|
9668
|
+
this.write(group.triggerName);
|
|
9630
9669
|
if (group.triggerEndTime !== Number.MAX_SAFE_INTEGER) {
|
|
9631
9670
|
this.write(",");
|
|
9632
|
-
this.write(`${group.triggerStartTime},`);
|
|
9633
|
-
this.write(
|
|
9671
|
+
this.write(`${group.triggerStartTime.toString()},`);
|
|
9672
|
+
this.write(group.triggerEndTime.toString());
|
|
9634
9673
|
}
|
|
9635
9674
|
if (group.groupNumber !== 0) {
|
|
9636
9675
|
this.write(",");
|
|
9637
|
-
this.write(
|
|
9676
|
+
this.write(group.groupNumber.toString());
|
|
9638
9677
|
}
|
|
9639
9678
|
}
|
|
9640
9679
|
this.encodeTimeline(group.alpha);
|
|
@@ -9655,9 +9694,11 @@ class StoryboardEventsEncoder extends StoryboardBaseEncoder {
|
|
|
9655
9694
|
encodeCommand(command) {
|
|
9656
9695
|
this.write(" ");
|
|
9657
9696
|
this.write(`${command.type},`);
|
|
9658
|
-
this.write(`${command.easing},`);
|
|
9659
|
-
this.write(`${command.startTime},`);
|
|
9660
|
-
this.write(command.startTime !== command.endTime
|
|
9697
|
+
this.write(`${command.easing.toString()},`);
|
|
9698
|
+
this.write(`${command.startTime.toString()},`);
|
|
9699
|
+
this.write(command.startTime !== command.endTime
|
|
9700
|
+
? command.endTime.toString()
|
|
9701
|
+
: "");
|
|
9661
9702
|
this.write(",");
|
|
9662
9703
|
if (command.startValue instanceof Vector2 &&
|
|
9663
9704
|
command.endValue instanceof Vector2) {
|
|
@@ -9752,14 +9793,14 @@ class BeatmapEventsEncoder extends BeatmapBaseEncoder {
|
|
|
9752
9793
|
this.writeLine("//Background and Video Events");
|
|
9753
9794
|
const { events } = this.map;
|
|
9754
9795
|
if (events.background) {
|
|
9755
|
-
this.writeLine(`0,0,"${events.background.filename}",${events.background.offset.x},${events.background.offset.y}`);
|
|
9796
|
+
this.writeLine(`0,0,"${events.background.filename}",${events.background.offset.x.toString()},${events.background.offset.y.toString()}`);
|
|
9756
9797
|
}
|
|
9757
9798
|
if (events.video) {
|
|
9758
|
-
this.writeLine(`Video,${events.video.startTime},"${events.video.filename}",${events.video.offset.x},${events.video.offset.y}`);
|
|
9799
|
+
this.writeLine(`Video,${events.video.startTime.toString()},"${events.video.filename}",${events.video.offset.x.toString()},${events.video.offset.y.toString()}`);
|
|
9759
9800
|
}
|
|
9760
9801
|
this.writeLine("//Break Periods");
|
|
9761
9802
|
for (const b of events.breaks) {
|
|
9762
|
-
this.writeLine(`2,${b.startTime},${b.endTime}`);
|
|
9803
|
+
this.writeLine(`2,${b.startTime.toString()},${b.endTime.toString()}`);
|
|
9763
9804
|
}
|
|
9764
9805
|
if (this.map.events.storyboard) {
|
|
9765
9806
|
this.writeLine(new StoryboardEncoder(this.map.events.storyboard, false).encode().result);
|
|
@@ -9787,20 +9828,20 @@ class BeatmapGeneralEncoder extends BeatmapBaseEncoder {
|
|
|
9787
9828
|
if (general.audioFilename) {
|
|
9788
9829
|
this.writeLine(`AudioFilename: ${general.audioFilename}`);
|
|
9789
9830
|
}
|
|
9790
|
-
this.writeLine(`AudioLeadIn: ${general.audioLeadIn}`);
|
|
9791
|
-
this.writeLine(`PreviewTime: ${general.previewTime}`);
|
|
9792
|
-
this.writeLine(`Countdown: ${general.countdown}`);
|
|
9831
|
+
this.writeLine(`AudioLeadIn: ${general.audioLeadIn.toString()}`);
|
|
9832
|
+
this.writeLine(`PreviewTime: ${general.previewTime.toString()}`);
|
|
9833
|
+
this.writeLine(`Countdown: ${general.countdown.toString()}`);
|
|
9793
9834
|
this.writeLine(`SampleSet: ${this.sampleBankToString(general.sampleBank)}`);
|
|
9794
|
-
this.writeLine(`StackLeniency: ${general.stackLeniency}`);
|
|
9795
|
-
this.writeLine(`Mode: ${general.mode}`);
|
|
9796
|
-
this.writeLine(`LetterboxInBreaks: ${general.letterBoxInBreaks ? 1 : 0}`);
|
|
9835
|
+
this.writeLine(`StackLeniency: ${general.stackLeniency.toString()}`);
|
|
9836
|
+
this.writeLine(`Mode: ${general.mode.toString()}`);
|
|
9837
|
+
this.writeLine(`LetterboxInBreaks: ${general.letterBoxInBreaks ? "1" : "0"}`);
|
|
9797
9838
|
if (general.epilepsyWarning) {
|
|
9798
9839
|
this.writeLine("EpilepsyWarning: 1");
|
|
9799
9840
|
}
|
|
9800
9841
|
if (general.countdownOffset > 0) {
|
|
9801
|
-
this.writeLine(`CountdownOffset: ${general.countdownOffset}`);
|
|
9842
|
+
this.writeLine(`CountdownOffset: ${general.countdownOffset.toString()}`);
|
|
9802
9843
|
}
|
|
9803
|
-
this.writeLine(`WidescreenStoryboard: ${general.widescreenStoryboard ? 1 : 0}`);
|
|
9844
|
+
this.writeLine(`WidescreenStoryboard: ${general.widescreenStoryboard ? "1" : "0"}`);
|
|
9804
9845
|
if (general.samplesMatchPlaybackRate) {
|
|
9805
9846
|
this.writeLine("SamplesMatchPlaybackRate: 1");
|
|
9806
9847
|
}
|
|
@@ -9820,10 +9861,10 @@ class BeatmapHitObjectsEncoder extends BeatmapBaseEncoder {
|
|
|
9820
9861
|
}
|
|
9821
9862
|
}
|
|
9822
9863
|
encodeHitObject(object) {
|
|
9823
|
-
this.write(`${object.position.x},`);
|
|
9824
|
-
this.write(`${object.position.y},`);
|
|
9825
|
-
this.write(`${object.startTime},`);
|
|
9826
|
-
this.write(`${object.type},`);
|
|
9864
|
+
this.write(`${object.position.x.toString()},`);
|
|
9865
|
+
this.write(`${object.position.y.toString()},`);
|
|
9866
|
+
this.write(`${object.startTime.toString()},`);
|
|
9867
|
+
this.write(`${object.type.toString()},`);
|
|
9827
9868
|
this.write(`${this.samplesToHitSoundType(object.samples).toString()},`);
|
|
9828
9869
|
if (object instanceof Slider) {
|
|
9829
9870
|
this.addSliderPath(object);
|
|
@@ -9831,7 +9872,7 @@ class BeatmapHitObjectsEncoder extends BeatmapBaseEncoder {
|
|
|
9831
9872
|
}
|
|
9832
9873
|
else {
|
|
9833
9874
|
if (object instanceof Spinner) {
|
|
9834
|
-
this.write(`${object.endTime},`);
|
|
9875
|
+
this.write(`${object.endTime.toString()},`);
|
|
9835
9876
|
}
|
|
9836
9877
|
this.write(this.getSampleBank(object.samples));
|
|
9837
9878
|
}
|
|
@@ -9864,11 +9905,11 @@ class BeatmapHitObjectsEncoder extends BeatmapBaseEncoder {
|
|
|
9864
9905
|
// start position of the slider.
|
|
9865
9906
|
for (let i = 1; i < slider.path.controlPoints.length; ++i) {
|
|
9866
9907
|
const realPosition = slider.path.controlPoints[i].add(slider.position);
|
|
9867
|
-
this.write(`${realPosition.x}:${realPosition.y}`);
|
|
9908
|
+
this.write(`${realPosition.x.toString()}:${realPosition.y.toString()}`);
|
|
9868
9909
|
this.write(i != slider.path.controlPoints.length - 1 ? "|" : ",");
|
|
9869
9910
|
}
|
|
9870
|
-
this.write(`${slider.
|
|
9871
|
-
this.write(`${slider.path.expectedDistance},`);
|
|
9911
|
+
this.write(`${slider.spanCount.toString()},`);
|
|
9912
|
+
this.write(`${slider.path.expectedDistance.toString()},`);
|
|
9872
9913
|
// edgeSamples
|
|
9873
9914
|
for (let i = 0; i < slider.nodeSamples.length; ++i) {
|
|
9874
9915
|
this.write(this.samplesToHitSoundType(slider.nodeSamples[i]).toString());
|
|
@@ -9887,14 +9928,14 @@ class BeatmapHitObjectsEncoder extends BeatmapBaseEncoder {
|
|
|
9887
9928
|
const addBank = (_d = (_c = samples.find((s) => s instanceof BankHitSampleInfo &&
|
|
9888
9929
|
s.name &&
|
|
9889
9930
|
s.name !== BankHitSampleInfo.HIT_NORMAL)) === null || _c === void 0 ? void 0 : _c.bank) !== null && _d !== void 0 ? _d : exports.SampleBank.none;
|
|
9890
|
-
let sampleBankString = `${normalBank}:${addBank}`;
|
|
9931
|
+
let sampleBankString = `${normalBank.toString()}:${addBank.toString()}`;
|
|
9891
9932
|
if (!banksOnly) {
|
|
9892
|
-
const firstSample = samples
|
|
9933
|
+
const firstSample = samples.at(0);
|
|
9893
9934
|
sampleBankString += ":";
|
|
9894
9935
|
sampleBankString += `${firstSample instanceof BankHitSampleInfo
|
|
9895
|
-
? firstSample.customSampleBank
|
|
9896
|
-
: 0}:`;
|
|
9897
|
-
sampleBankString += `${(_e = firstSample === null || firstSample === void 0 ? void 0 : firstSample.volume) !== null && _e !== void 0 ? _e : 100}:`;
|
|
9936
|
+
? firstSample.customSampleBank.toString()
|
|
9937
|
+
: "0"}:`;
|
|
9938
|
+
sampleBankString += `${((_e = firstSample === null || firstSample === void 0 ? void 0 : firstSample.volume) !== null && _e !== void 0 ? _e : 100).toString()}:`;
|
|
9898
9939
|
if (firstSample instanceof FileHitSampleInfo) {
|
|
9899
9940
|
sampleBankString += `${this.sampleBankToString(exports.SampleBank.none)}-${firstSample.filename}`;
|
|
9900
9941
|
}
|
|
@@ -9916,7 +9957,6 @@ class BeatmapHitObjectsEncoder extends BeatmapBaseEncoder {
|
|
|
9916
9957
|
*/
|
|
9917
9958
|
class BeatmapMetadataEncoder extends BeatmapBaseEncoder {
|
|
9918
9959
|
encodeInternal() {
|
|
9919
|
-
var _a, _b;
|
|
9920
9960
|
if (this.encodeSections) {
|
|
9921
9961
|
this.writeLine("[Metadata]");
|
|
9922
9962
|
}
|
|
@@ -9937,11 +9977,11 @@ class BeatmapMetadataEncoder extends BeatmapBaseEncoder {
|
|
|
9937
9977
|
if (metadata.tags.length > 0) {
|
|
9938
9978
|
this.writeLine(`Tags: ${metadata.tags.join(" ")}`);
|
|
9939
9979
|
}
|
|
9940
|
-
if (
|
|
9941
|
-
this.writeLine(`BeatmapID: ${metadata.beatmapId}`);
|
|
9980
|
+
if (metadata.beatmapId !== undefined && metadata.beatmapId > 0) {
|
|
9981
|
+
this.writeLine(`BeatmapID: ${metadata.beatmapId.toString()}`);
|
|
9942
9982
|
}
|
|
9943
|
-
if (
|
|
9944
|
-
this.writeLine(`BeatmapSetID: ${metadata.beatmapSetId}`);
|
|
9983
|
+
if (metadata.beatmapSetId !== undefined && metadata.beatmapSetId > 0) {
|
|
9984
|
+
this.writeLine(`BeatmapSetID: ${metadata.beatmapSetId.toString()}`);
|
|
9945
9985
|
}
|
|
9946
9986
|
}
|
|
9947
9987
|
}
|
|
@@ -9959,7 +9999,7 @@ class BeatmapEncoder extends Encoder {
|
|
|
9959
9999
|
this.latestVersion = 14;
|
|
9960
10000
|
}
|
|
9961
10001
|
encodeInternal() {
|
|
9962
|
-
this.writeLine(`osu file format v${this.latestVersion}`);
|
|
10002
|
+
this.writeLine(`osu file format v${this.latestVersion.toString()}`);
|
|
9963
10003
|
this.writeLine();
|
|
9964
10004
|
super.encodeInternal();
|
|
9965
10005
|
}
|
|
@@ -10024,6 +10064,56 @@ exports.BeatmapLanguage = void 0;
|
|
|
10024
10064
|
BeatmapLanguage[BeatmapLanguage["other"] = 14] = "other";
|
|
10025
10065
|
})(exports.BeatmapLanguage || (exports.BeatmapLanguage = {}));
|
|
10026
10066
|
|
|
10067
|
+
class Bin {
|
|
10068
|
+
constructor() {
|
|
10069
|
+
this.difficulty = 0;
|
|
10070
|
+
this.time = 0;
|
|
10071
|
+
this.noteCount = 0;
|
|
10072
|
+
}
|
|
10073
|
+
/**
|
|
10074
|
+
* Creates a 2D grid of bins using bilinear interpolation.
|
|
10075
|
+
*
|
|
10076
|
+
* Notes are distributed across neighboring bins weighted by their fractional position.
|
|
10077
|
+
*/
|
|
10078
|
+
static createBins(difficulties, times, difficultyDimensionLength, timeDimensionLength) {
|
|
10079
|
+
const maxDifficulty = MathUtils.max(difficulties);
|
|
10080
|
+
const endTime = MathUtils.max(times);
|
|
10081
|
+
const bins = Utils.initializeArray(timeDimensionLength * difficultyDimensionLength, () => new Bin());
|
|
10082
|
+
for (let timeIndex = 0; timeIndex < timeDimensionLength; ++timeIndex) {
|
|
10083
|
+
const time = (endTime * timeIndex) / (timeDimensionLength - 1);
|
|
10084
|
+
for (let diffIndex = 0; diffIndex < difficultyDimensionLength; ++diffIndex) {
|
|
10085
|
+
const binIndex = difficultyDimensionLength * timeIndex + diffIndex;
|
|
10086
|
+
bins[binIndex].time = time;
|
|
10087
|
+
// We do not create a 0 difficulty bin because 0 difficulty notes do not contribute to star rating.
|
|
10088
|
+
bins[binIndex].difficulty =
|
|
10089
|
+
(maxDifficulty * (diffIndex + 1)) /
|
|
10090
|
+
difficultyDimensionLength;
|
|
10091
|
+
}
|
|
10092
|
+
}
|
|
10093
|
+
for (let noteIndex = 0; noteIndex < difficulties.length; ++noteIndex) {
|
|
10094
|
+
const timeBinIndex = timeDimensionLength * (times[noteIndex] / endTime);
|
|
10095
|
+
const difficultyBinIndex = difficultyDimensionLength *
|
|
10096
|
+
(difficulties[noteIndex] / maxDifficulty) -
|
|
10097
|
+
1;
|
|
10098
|
+
const timeLower = Math.min(Math.trunc(timeBinIndex), timeDimensionLength - 1);
|
|
10099
|
+
const timeUpper = Math.min(timeLower + 1, timeDimensionLength - 1);
|
|
10100
|
+
const timeWeight = timeBinIndex - timeLower;
|
|
10101
|
+
const difficultyLower = Math.floor(difficultyBinIndex);
|
|
10102
|
+
const difficultyUpper = Math.min(difficultyLower + 1, difficultyDimensionLength - 1);
|
|
10103
|
+
const difficultyWeight = difficultyBinIndex - difficultyLower;
|
|
10104
|
+
// The lower bound of difficulty can be -1, corresponding to buckets with 0 difficulty.
|
|
10105
|
+
// We do not store those since they do not contribute to star rating.
|
|
10106
|
+
if (difficultyLower >= 0) {
|
|
10107
|
+
bins[difficultyDimensionLength * timeLower + difficultyLower].noteCount += (1 - timeWeight) * (1 - difficultyWeight);
|
|
10108
|
+
bins[difficultyDimensionLength * timeUpper + difficultyLower].noteCount += timeWeight * (1 - difficultyWeight);
|
|
10109
|
+
}
|
|
10110
|
+
bins[difficultyDimensionLength * timeLower + difficultyUpper].noteCount += (1 - timeWeight) * difficultyWeight;
|
|
10111
|
+
bins[difficultyDimensionLength * timeUpper + difficultyUpper].noteCount += timeWeight * difficultyWeight;
|
|
10112
|
+
}
|
|
10113
|
+
return bins;
|
|
10114
|
+
}
|
|
10115
|
+
}
|
|
10116
|
+
|
|
10027
10117
|
class ZeroCrossingBracketing {
|
|
10028
10118
|
/**
|
|
10029
10119
|
* Detect a range containing at least one root.
|
|
@@ -10254,6 +10344,93 @@ class Brent {
|
|
|
10254
10344
|
}
|
|
10255
10345
|
}
|
|
10256
10346
|
|
|
10347
|
+
/**
|
|
10348
|
+
* {@link https://www.sciencedirect.com/science/article/abs/pii/S0965997896000518?via%3Dihub Chandrupatla}'s root-finding algorithm.
|
|
10349
|
+
*/
|
|
10350
|
+
class Chandrupatla {
|
|
10351
|
+
/**
|
|
10352
|
+
* Finds the root of a function using {@link https://www.sciencedirect.com/science/article/abs/pii/S0965997896000518?via%3Dihub Chandrupatla}'s
|
|
10353
|
+
* method, expanding the bounds if the root is not located within.
|
|
10354
|
+
*
|
|
10355
|
+
* Expansion only occurs for the upward bound, as this function is optimized for functions of range [0, x), which is
|
|
10356
|
+
* useful for finding positive roots.
|
|
10357
|
+
*
|
|
10358
|
+
* @param f The function of which to find the root.
|
|
10359
|
+
* @param guessLowerBound The lower bound of the function inputs.
|
|
10360
|
+
* @param guessUpperBound The upper bound of the function inputs.
|
|
10361
|
+
* @param options Options for the root-finding algorithm.
|
|
10362
|
+
* @returns The root of the function.
|
|
10363
|
+
*/
|
|
10364
|
+
static findRootExpand(f, guessLowerBound, guessUpperBound, options) {
|
|
10365
|
+
const { maxIterations = 25, accuracy = 1e-6, expansionFactor = 2, maxExpansions = 32, } = options !== null && options !== void 0 ? options : {};
|
|
10366
|
+
let a = guessLowerBound;
|
|
10367
|
+
let b = guessUpperBound;
|
|
10368
|
+
let fa = f(a);
|
|
10369
|
+
let fb = f(b);
|
|
10370
|
+
let expansions = 0;
|
|
10371
|
+
while (fa * fb > 0) {
|
|
10372
|
+
a = b;
|
|
10373
|
+
b *= expansionFactor;
|
|
10374
|
+
fa = fb;
|
|
10375
|
+
fb = f(b);
|
|
10376
|
+
++expansions;
|
|
10377
|
+
if (expansions > maxExpansions) {
|
|
10378
|
+
throw new Error("Chandrupatla: Maximum number of expansions exceeded.");
|
|
10379
|
+
}
|
|
10380
|
+
}
|
|
10381
|
+
let t = 0.5;
|
|
10382
|
+
for (let i = 0; i < maxIterations; ++i) {
|
|
10383
|
+
const xt = a + t * (b - a);
|
|
10384
|
+
const ft = f(xt);
|
|
10385
|
+
let c;
|
|
10386
|
+
let fc;
|
|
10387
|
+
if (Math.sign(ft) == Math.sign(fa)) {
|
|
10388
|
+
c = a;
|
|
10389
|
+
fc = fa;
|
|
10390
|
+
}
|
|
10391
|
+
else {
|
|
10392
|
+
c = b;
|
|
10393
|
+
b = a;
|
|
10394
|
+
fc = fb;
|
|
10395
|
+
fb = fa;
|
|
10396
|
+
}
|
|
10397
|
+
a = xt;
|
|
10398
|
+
fa = ft;
|
|
10399
|
+
let xm;
|
|
10400
|
+
let fm;
|
|
10401
|
+
if (Math.abs(fa) < Math.abs(fb)) {
|
|
10402
|
+
xm = a;
|
|
10403
|
+
fm = fa;
|
|
10404
|
+
}
|
|
10405
|
+
else {
|
|
10406
|
+
xm = b;
|
|
10407
|
+
fm = fb;
|
|
10408
|
+
}
|
|
10409
|
+
if (fm == 0) {
|
|
10410
|
+
return xm;
|
|
10411
|
+
}
|
|
10412
|
+
const tol = 2 * accuracy * Math.abs(xm) + 2 * accuracy;
|
|
10413
|
+
const tlim = tol / Math.abs(b - c);
|
|
10414
|
+
if (tlim > 0.5) {
|
|
10415
|
+
return xm;
|
|
10416
|
+
}
|
|
10417
|
+
const chi = (a - b) / (c - b);
|
|
10418
|
+
const phi = (fa - fb) / (fc - fb);
|
|
10419
|
+
const iqi = phi * phi < chi && (1 - phi) * (1 - phi) < chi;
|
|
10420
|
+
if (iqi) {
|
|
10421
|
+
t =
|
|
10422
|
+
((fa / (fb - fa)) * fc) / (fb - fc) +
|
|
10423
|
+
(((((c - a) / (b - a)) * fa) / (fc - fa)) * fb) / (fc - fb);
|
|
10424
|
+
}
|
|
10425
|
+
else {
|
|
10426
|
+
t = 0.5;
|
|
10427
|
+
}
|
|
10428
|
+
t = Math.min(1 - tlim, Math.max(tlim, t));
|
|
10429
|
+
}
|
|
10430
|
+
return 0;
|
|
10431
|
+
}
|
|
10432
|
+
}
|
|
10433
|
+
|
|
10257
10434
|
/******************************************************************************
|
|
10258
10435
|
Copyright (c) Microsoft Corporation.
|
|
10259
10436
|
|
|
@@ -10268,7 +10445,7 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
10268
10445
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
10269
10446
|
PERFORMANCE OF THIS SOFTWARE.
|
|
10270
10447
|
***************************************************************************** */
|
|
10271
|
-
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
10448
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
10272
10449
|
|
|
10273
10450
|
|
|
10274
10451
|
function __awaiter(thisArg, _arguments, P, generator) {
|
|
@@ -10289,6 +10466,7 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
10289
10466
|
/**
|
|
10290
10467
|
* The base of API request builders.
|
|
10291
10468
|
*/
|
|
10469
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
|
|
10292
10470
|
class APIRequestBuilder {
|
|
10293
10471
|
constructor() {
|
|
10294
10472
|
/**
|
|
@@ -10357,22 +10535,24 @@ class APIRequestBuilder {
|
|
|
10357
10535
|
.then((res) => __awaiter(this, void 0, void 0, function* () {
|
|
10358
10536
|
++this.fetchAttempts;
|
|
10359
10537
|
if (res.status >= 500 && this.fetchAttempts < 5) {
|
|
10360
|
-
console.error(`Request to ${url} failed with the following error: ${yield res.text()}; ${this.fetchAttempts} attempts so far; retrying`);
|
|
10361
|
-
|
|
10538
|
+
console.error(`Request to ${url} failed with the following error: ${yield res.text()}; ${this.fetchAttempts.toString()} attempts so far; retrying`);
|
|
10539
|
+
resolve(this.sendRequest());
|
|
10540
|
+
return;
|
|
10362
10541
|
}
|
|
10363
10542
|
this.fetchAttempts = 0;
|
|
10364
|
-
|
|
10543
|
+
resolve({
|
|
10365
10544
|
data: Buffer.from(yield res.arrayBuffer()),
|
|
10366
10545
|
statusCode: res.status,
|
|
10367
10546
|
});
|
|
10368
10547
|
}))
|
|
10369
10548
|
.catch((e) => {
|
|
10370
|
-
console.error(`Request to ${url} failed with the following error: ${e.message}; ${this.fetchAttempts} attempts so far; aborting`);
|
|
10549
|
+
console.error(`Request to ${url} failed with the following error: ${e.message}; ${this.fetchAttempts.toString()} attempts so far; aborting`);
|
|
10371
10550
|
this.fetchAttempts = 0;
|
|
10372
|
-
|
|
10551
|
+
resolve({
|
|
10373
10552
|
data: Buffer.from([]),
|
|
10374
10553
|
statusCode: 400,
|
|
10375
10554
|
});
|
|
10555
|
+
return;
|
|
10376
10556
|
});
|
|
10377
10557
|
});
|
|
10378
10558
|
}
|
|
@@ -10581,6 +10761,196 @@ class Polynomial {
|
|
|
10581
10761
|
}
|
|
10582
10762
|
return sum;
|
|
10583
10763
|
}
|
|
10764
|
+
/**
|
|
10765
|
+
* Solve for the exact real roots of any polynomial up to degree 4.
|
|
10766
|
+
*
|
|
10767
|
+
* @param coefficients The coefficients of the polynomial, in ascending order ([1, 3, 5] -> x^2 + 3x + 5).
|
|
10768
|
+
* @returns The real roots of the polynomial, and `null` if the root does not exist.
|
|
10769
|
+
*/
|
|
10770
|
+
static solve(coefficients) {
|
|
10771
|
+
let xVals = [];
|
|
10772
|
+
switch (coefficients.length) {
|
|
10773
|
+
case 5:
|
|
10774
|
+
xVals = this.solveP4(coefficients[0], coefficients[1], coefficients[2], coefficients[3], coefficients[4]).roots;
|
|
10775
|
+
break;
|
|
10776
|
+
case 4:
|
|
10777
|
+
xVals = this.solveP3(coefficients[0], coefficients[1], coefficients[2], coefficients[3]).roots;
|
|
10778
|
+
break;
|
|
10779
|
+
case 3:
|
|
10780
|
+
xVals = this.solveP2(coefficients[0], coefficients[1], coefficients[2]).roots;
|
|
10781
|
+
break;
|
|
10782
|
+
case 2:
|
|
10783
|
+
xVals = this.solveP2(0, coefficients[0], coefficients[1]).roots;
|
|
10784
|
+
break;
|
|
10785
|
+
}
|
|
10786
|
+
return xVals;
|
|
10787
|
+
}
|
|
10788
|
+
// https://github.com/sasamil/Quartic/blob/master/quartic.cpp
|
|
10789
|
+
static solveP4(a, b, c, d, e) {
|
|
10790
|
+
const result = {
|
|
10791
|
+
count: 0,
|
|
10792
|
+
roots: new Array(4),
|
|
10793
|
+
};
|
|
10794
|
+
if (a === 0) {
|
|
10795
|
+
const xValsCubic = this.solveP3(b, c, d, e);
|
|
10796
|
+
result.count = xValsCubic.count;
|
|
10797
|
+
result.roots[0] = xValsCubic.roots[0];
|
|
10798
|
+
result.roots[1] = xValsCubic.roots[1];
|
|
10799
|
+
result.roots[2] = xValsCubic.roots[2];
|
|
10800
|
+
result.roots[3] = null;
|
|
10801
|
+
return result;
|
|
10802
|
+
}
|
|
10803
|
+
b /= a;
|
|
10804
|
+
c /= a;
|
|
10805
|
+
d /= a;
|
|
10806
|
+
e /= a;
|
|
10807
|
+
const a3 = -c;
|
|
10808
|
+
const b3 = b * d - 4 * e;
|
|
10809
|
+
const c3 = -b * b * e - d * d + 4 * c * e;
|
|
10810
|
+
const x3 = this.solveP3(1, a3, b3, c3);
|
|
10811
|
+
let q1;
|
|
10812
|
+
let q2;
|
|
10813
|
+
let p1;
|
|
10814
|
+
let p2;
|
|
10815
|
+
let sqD;
|
|
10816
|
+
let y = x3.roots[0];
|
|
10817
|
+
// Get the y value with the highest absolute value.
|
|
10818
|
+
if (x3.count !== 1) {
|
|
10819
|
+
if (Math.abs(x3.roots[1]) > Math.abs(y)) {
|
|
10820
|
+
y = x3.roots[1];
|
|
10821
|
+
}
|
|
10822
|
+
if (Math.abs(x3.roots[2]) > Math.abs(y)) {
|
|
10823
|
+
y = x3.roots[2];
|
|
10824
|
+
}
|
|
10825
|
+
}
|
|
10826
|
+
let upperD = y * y - 4 * e;
|
|
10827
|
+
if (Precision.almostEquals(upperD, 0)) {
|
|
10828
|
+
q1 = q2 = y * 0.5;
|
|
10829
|
+
upperD = b * b - 4 * (c - y);
|
|
10830
|
+
if (Precision.almostEquals(upperD, 0)) {
|
|
10831
|
+
p1 = p2 = b * 0.5;
|
|
10832
|
+
}
|
|
10833
|
+
else {
|
|
10834
|
+
sqD = Math.sqrt(upperD);
|
|
10835
|
+
p1 = (b + sqD) * 0.5;
|
|
10836
|
+
p2 = (b - sqD) * 0.5;
|
|
10837
|
+
}
|
|
10838
|
+
}
|
|
10839
|
+
else {
|
|
10840
|
+
sqD = Math.sqrt(upperD);
|
|
10841
|
+
q1 = (y + sqD) * 0.5;
|
|
10842
|
+
q2 = (y - sqD) * 0.5;
|
|
10843
|
+
p1 = (b * q1 - d) / (q1 - q2);
|
|
10844
|
+
p2 = (d - b * q2) / (q1 - q2);
|
|
10845
|
+
}
|
|
10846
|
+
// Solving quadratic eq. - x^2 + p1*x + q1 = 0.
|
|
10847
|
+
upperD = p1 * p1 - 4 * q1;
|
|
10848
|
+
if (upperD >= 0) {
|
|
10849
|
+
result.count += 2;
|
|
10850
|
+
sqD = Math.sqrt(upperD);
|
|
10851
|
+
result.roots[0] = (-p1 + sqD) * 0.5;
|
|
10852
|
+
result.roots[1] = (-p1 - sqD) * 0.5;
|
|
10853
|
+
}
|
|
10854
|
+
// Solving quadratic eq. - x^2 + p2*x + q2 = 0.
|
|
10855
|
+
upperD = p2 * p2 - 4 * q2;
|
|
10856
|
+
if (upperD >= 0) {
|
|
10857
|
+
result.count += 2;
|
|
10858
|
+
sqD = Math.sqrt(upperD);
|
|
10859
|
+
result.roots[2] = (-p2 + sqD) * 0.5;
|
|
10860
|
+
result.roots[3] = (-p2 - sqD) * 0.5;
|
|
10861
|
+
}
|
|
10862
|
+
// Put the null roots at the end of the array.
|
|
10863
|
+
result.roots.sort((a, b) => {
|
|
10864
|
+
if (a === null && b === null) {
|
|
10865
|
+
return 0;
|
|
10866
|
+
}
|
|
10867
|
+
if (a === null) {
|
|
10868
|
+
return 1;
|
|
10869
|
+
}
|
|
10870
|
+
if (b === null) {
|
|
10871
|
+
return -1;
|
|
10872
|
+
}
|
|
10873
|
+
return 0;
|
|
10874
|
+
});
|
|
10875
|
+
return result;
|
|
10876
|
+
}
|
|
10877
|
+
static solveP3(a, b, c, d) {
|
|
10878
|
+
const result = {
|
|
10879
|
+
count: 0,
|
|
10880
|
+
roots: new Array(3),
|
|
10881
|
+
};
|
|
10882
|
+
if (a === 0) {
|
|
10883
|
+
const xValsQuadratic = this.solveP2(b, c, d);
|
|
10884
|
+
result.count = xValsQuadratic.count;
|
|
10885
|
+
result.roots[0] = xValsQuadratic.roots[0];
|
|
10886
|
+
result.roots[1] = xValsQuadratic.roots[1];
|
|
10887
|
+
result.roots[2] = null;
|
|
10888
|
+
return result;
|
|
10889
|
+
}
|
|
10890
|
+
b /= a;
|
|
10891
|
+
c /= a;
|
|
10892
|
+
d /= a;
|
|
10893
|
+
const a2 = b * b;
|
|
10894
|
+
let q = (a2 - 3 * c) / 9;
|
|
10895
|
+
const q3 = q * q * q;
|
|
10896
|
+
const r = (b * (2 * a2 - 9 * c) + 27 * d) / 54;
|
|
10897
|
+
const r2 = r * r;
|
|
10898
|
+
if (r2 < q3) {
|
|
10899
|
+
let t = r / Math.sqrt(q3);
|
|
10900
|
+
t = MathUtils.clamp(t, -1, 1);
|
|
10901
|
+
t = Math.acos(t);
|
|
10902
|
+
b /= 3;
|
|
10903
|
+
q = -2 * Math.sqrt(q);
|
|
10904
|
+
result.count = 3;
|
|
10905
|
+
result.roots[0] = q * Math.cos(t / 3) - b;
|
|
10906
|
+
result.roots[1] = q * Math.cos((t + Math.PI * 2) / 3) - b;
|
|
10907
|
+
result.roots[2] = q * Math.cos((t - Math.PI * 2) / 3) - b;
|
|
10908
|
+
return result;
|
|
10909
|
+
}
|
|
10910
|
+
let upperA = -Math.cbrt(Math.abs(r) + Math.sqrt(r2 - q3));
|
|
10911
|
+
if (r < 0) {
|
|
10912
|
+
upperA = -upperA;
|
|
10913
|
+
}
|
|
10914
|
+
const upperB = upperA == 0 ? 0 : q / upperA;
|
|
10915
|
+
b /= 3;
|
|
10916
|
+
result.count = 1;
|
|
10917
|
+
result.roots[0] = upperA + upperB - b;
|
|
10918
|
+
if (Precision.almostEquals(0.5 * Math.sqrt(3) * (upperA - upperB), 0)) {
|
|
10919
|
+
result.count = 2;
|
|
10920
|
+
result.roots[1] = -0.5 * (upperA + upperB) - b;
|
|
10921
|
+
return result;
|
|
10922
|
+
}
|
|
10923
|
+
return result;
|
|
10924
|
+
}
|
|
10925
|
+
static solveP2(a, b, c) {
|
|
10926
|
+
const result = {
|
|
10927
|
+
count: 0,
|
|
10928
|
+
roots: new Array(2),
|
|
10929
|
+
};
|
|
10930
|
+
if (a === 0) {
|
|
10931
|
+
if (b === 0) {
|
|
10932
|
+
return result;
|
|
10933
|
+
}
|
|
10934
|
+
result.count = 1;
|
|
10935
|
+
result.roots[0] = -c / b;
|
|
10936
|
+
}
|
|
10937
|
+
const discriminant = b * b - 4 * a * c;
|
|
10938
|
+
if (discriminant < 0) {
|
|
10939
|
+
return result;
|
|
10940
|
+
}
|
|
10941
|
+
switch (discriminant) {
|
|
10942
|
+
case 0:
|
|
10943
|
+
result.count = 1;
|
|
10944
|
+
result.roots[0] = -b / (2 * a);
|
|
10945
|
+
break;
|
|
10946
|
+
default:
|
|
10947
|
+
result.count = 2;
|
|
10948
|
+
result.roots[0] = (-b + Math.sqrt(discriminant)) / (2 * a);
|
|
10949
|
+
result.roots[1] = (-b - Math.sqrt(discriminant)) / (2 * a);
|
|
10950
|
+
break;
|
|
10951
|
+
}
|
|
10952
|
+
return result;
|
|
10953
|
+
}
|
|
10584
10954
|
}
|
|
10585
10955
|
|
|
10586
10956
|
/**
|
|
@@ -10976,7 +11346,7 @@ class ErrorFunction {
|
|
|
10976
11346
|
* calculation for erf(x) in the interval [1e-10, 0.5].
|
|
10977
11347
|
*/
|
|
10978
11348
|
ErrorFunction.erfImpAn = [
|
|
10979
|
-
0.003379167095512574, -
|
|
11349
|
+
0.003379167095512574, -7369565304816795e-19, -0.3747323373929196,
|
|
10980
11350
|
0.0817442448733587, -0.04210893199365486, 0.007016570951209576,
|
|
10981
11351
|
-0.004950912559824351, 0.0008716465990379225,
|
|
10982
11352
|
];
|
|
@@ -10987,7 +11357,7 @@ ErrorFunction.erfImpAn = [
|
|
|
10987
11357
|
ErrorFunction.erfImpAd = [
|
|
10988
11358
|
1, -0.2180882180879246, 0.4125429727254421, -0.08418911478731068,
|
|
10989
11359
|
0.0655338856400242, -0.01200196044549418, 0.00408165558926174,
|
|
10990
|
-
-
|
|
11360
|
+
-6159007215577697e-19,
|
|
10991
11361
|
];
|
|
10992
11362
|
/**
|
|
10993
11363
|
* Polynomial coefficients for a numerator in erfImp
|
|
@@ -11038,7 +11408,7 @@ ErrorFunction.erfImpDn = [
|
|
|
11038
11408
|
ErrorFunction.erfImpDd = [
|
|
11039
11409
|
1, 1.7596709814716753, 1.3288357143796112, 0.5525285965087576,
|
|
11040
11410
|
0.1337930569413329, 0.017950964517628076, 0.001047124400199374,
|
|
11041
|
-
-
|
|
11411
|
+
-1.0664038182035734e-8,
|
|
11042
11412
|
];
|
|
11043
11413
|
/**
|
|
11044
11414
|
* Polynomial coefficients for a numerator in erfImp
|
|
@@ -11073,7 +11443,7 @@ ErrorFunction.erfImpFn = [
|
|
|
11073
11443
|
ErrorFunction.erfImpFd = [
|
|
11074
11444
|
1, 1.210196977736308, 0.6209146682211439, 0.1730384306611428,
|
|
11075
11445
|
0.0276550813773432, 0.002406259744243097, 0.8918118172513366e-4,
|
|
11076
|
-
-
|
|
11446
|
+
-4655288362833827e-27,
|
|
11077
11447
|
];
|
|
11078
11448
|
/**
|
|
11079
11449
|
* Polynomial coefficients for a numerator in erfImp
|
|
@@ -11112,7 +11482,7 @@ ErrorFunction.erfImpHd = [
|
|
|
11112
11482
|
* calculation for erfc(x) in the interval [11.5, 17].
|
|
11113
11483
|
*/
|
|
11114
11484
|
ErrorFunction.erfImpIn = [
|
|
11115
|
-
-
|
|
11485
|
+
-5690799360109496e-19, 0.0001694985403737623, 0.5184723545811009e-4,
|
|
11116
11486
|
0.38281931223192885e-5, 0.8249899312818944e-7,
|
|
11117
11487
|
];
|
|
11118
11488
|
/**
|
|
@@ -11121,14 +11491,14 @@ ErrorFunction.erfImpIn = [
|
|
|
11121
11491
|
*/
|
|
11122
11492
|
ErrorFunction.erfImpId = [
|
|
11123
11493
|
1, 0.3396372500511393, 0.04347264787031066, 0.002485493352246371,
|
|
11124
|
-
0.5356333053371529e-4, -
|
|
11494
|
+
0.5356333053371529e-4, -11749094440545958e-29,
|
|
11125
11495
|
];
|
|
11126
11496
|
/**
|
|
11127
11497
|
* Polynomial coefficients for a numerator in erfImp
|
|
11128
11498
|
* calculation for erfc(x) in the interval [17, 24].
|
|
11129
11499
|
*/
|
|
11130
11500
|
ErrorFunction.erfImpJn = [
|
|
11131
|
-
-
|
|
11501
|
+
-24131359948399134e-20, 0.5742249752025015e-4, 0.11599896292738377e-4,
|
|
11132
11502
|
0.581762134402594e-6, 0.8539715550856736e-8,
|
|
11133
11503
|
];
|
|
11134
11504
|
/**
|
|
@@ -11144,7 +11514,7 @@ ErrorFunction.erfImpJd = [
|
|
|
11144
11514
|
* calculation for erfc(x) in the interval [24, 38].
|
|
11145
11515
|
*/
|
|
11146
11516
|
ErrorFunction.erfImpKn = [
|
|
11147
|
-
-
|
|
11517
|
+
-14667469927776036e-20, 0.1626665521122805e-4, 0.26911624850916523e-5,
|
|
11148
11518
|
0.979584479468092e-7, 0.10199464762572346e-8,
|
|
11149
11519
|
];
|
|
11150
11520
|
/**
|
|
@@ -11160,7 +11530,7 @@ ErrorFunction.erfImpKd = [
|
|
|
11160
11530
|
* calculation for erfc(x) in the interval [38, 60].
|
|
11161
11531
|
*/
|
|
11162
11532
|
ErrorFunction.erfImpLn = [
|
|
11163
|
-
-
|
|
11533
|
+
-5839057976297718e-20, 0.4125103251054962e-5, 0.43179092242025094e-6,
|
|
11164
11534
|
0.9933651555900132e-8, 0.653480510020105e-10,
|
|
11165
11535
|
];
|
|
11166
11536
|
/**
|
|
@@ -11176,7 +11546,7 @@ ErrorFunction.erfImpLd = [
|
|
|
11176
11546
|
* calculation for erfc(x) in the interval [60, 85].
|
|
11177
11547
|
*/
|
|
11178
11548
|
ErrorFunction.erfImpMn = [
|
|
11179
|
-
-
|
|
11549
|
+
-19645779760922958e-21, 0.1572438876668007e-5, 0.5439025111927009e-7,
|
|
11180
11550
|
0.3174724923691177e-9,
|
|
11181
11551
|
];
|
|
11182
11552
|
/**
|
|
@@ -11192,7 +11562,7 @@ ErrorFunction.erfImpMd = [
|
|
|
11192
11562
|
* calculation for erfc(x) in the interval [85, 110].
|
|
11193
11563
|
*/
|
|
11194
11564
|
ErrorFunction.erfImpNn = [
|
|
11195
|
-
-
|
|
11565
|
+
-789224703978723e-20, 0.622088451660987e-6, 0.1457284456768824e-7,
|
|
11196
11566
|
0.603715505542715e-10,
|
|
11197
11567
|
];
|
|
11198
11568
|
/**
|
|
@@ -11209,7 +11579,7 @@ ErrorFunction.erfImpNd = [
|
|
|
11209
11579
|
* calculation for erf^-1(z) in the interval [0, 0.5].
|
|
11210
11580
|
*/
|
|
11211
11581
|
ErrorFunction.ervInvImpAn = [
|
|
11212
|
-
-
|
|
11582
|
+
-5087819496582806e-19, -0.008368748197417368, 0.033480662540974461,
|
|
11213
11583
|
-0.012692614766297402, -0.03656379714117627, 0.02198786811111689,
|
|
11214
11584
|
0.008226878746769157, -0.005387729650712429,
|
|
11215
11585
|
];
|
|
@@ -11247,8 +11617,8 @@ ErrorFunction.ervInvImpBd = [
|
|
|
11247
11617
|
ErrorFunction.ervInvImpCn = [
|
|
11248
11618
|
-0.1311027816799519, -0.1637940471933171, 0.11703015634199525,
|
|
11249
11619
|
0.387079738972604337, 0.3377855389120359, 0.1428695344081572,
|
|
11250
|
-
0.029015791000532906, 0.002145589953888053, -
|
|
11251
|
-
0.2852253317822171e-7, -
|
|
11620
|
+
0.029015791000532906, 0.002145589953888053, -6.794655751811264e-7,
|
|
11621
|
+
0.2852253317822171e-7, -681149956853777e-24,
|
|
11252
11622
|
];
|
|
11253
11623
|
/**
|
|
11254
11624
|
* Polynomial coefficients for a denominator of erfInvImp
|
|
@@ -11266,7 +11636,7 @@ ErrorFunction.ervInvImpCd = [
|
|
|
11266
11636
|
ErrorFunction.ervInvImpDn = [
|
|
11267
11637
|
-0.0350353787183178, -0.002224265292134479, 0.018557330651423107,
|
|
11268
11638
|
0.009508047013259196, 0.001871234928195592, 0.00015754461742496055,
|
|
11269
|
-
0.460469890584318e-5, -
|
|
11639
|
+
0.460469890584318e-5, -2304047769118826e-25, 0.266339227425782e-11,
|
|
11270
11640
|
];
|
|
11271
11641
|
/**
|
|
11272
11642
|
* Polynomial coefficients for a denominator of erfInvImp
|
|
@@ -11283,7 +11653,7 @@ ErrorFunction.ervInvImpDd = [
|
|
|
11283
11653
|
ErrorFunction.ervInvImpEn = [
|
|
11284
11654
|
-0.016743100507663373, -0.001129514387455803, 0.001056288621524929,
|
|
11285
11655
|
0.0002093863174875881, 0.14962478375834237e-4, 0.4496967899277065e-6,
|
|
11286
|
-
0.4625961635228786e-8, -
|
|
11656
|
+
0.4625961635228786e-8, -2811287356288318e-29,
|
|
11287
11657
|
0.9905570997331033e-16,
|
|
11288
11658
|
];
|
|
11289
11659
|
/**
|
|
@@ -11299,9 +11669,9 @@ ErrorFunction.ervInvImpEd = [
|
|
|
11299
11669
|
* calculation for erf^-1(z) in the interval [0.75, 1] with x between 18 and 44.
|
|
11300
11670
|
*/
|
|
11301
11671
|
ErrorFunction.ervInvImpFn = [
|
|
11302
|
-
-0.
|
|
11672
|
+
-0.002497821279189813, -779190719229054e-20, 0.2547230374130275e-4,
|
|
11303
11673
|
0.1623977773425109e-5, 0.3963410113048011685e-7, 0.4116328311909442e-9,
|
|
11304
|
-
0.145596286718675e-11, -
|
|
11674
|
+
0.145596286718675e-11, -11676501239718427e-34,
|
|
11305
11675
|
];
|
|
11306
11676
|
/**
|
|
11307
11677
|
* Polynomial coefficients for a denominator of erfInvImp
|
|
@@ -11316,9 +11686,9 @@ ErrorFunction.ervInvImpFd = [
|
|
|
11316
11686
|
* calculation for erf^-1(z) in the interval [0.75, 1] with x greater than 44.
|
|
11317
11687
|
*/
|
|
11318
11688
|
ErrorFunction.ervInvImpGn = [
|
|
11319
|
-
-
|
|
11689
|
+
-5390429110190786e-19, -2.839875900472772e-7, 0.8994651148922914e-6,
|
|
11320
11690
|
0.2293458592659209e-7, 0.2255614448635001e-9, 0.9478466275030226e-12,
|
|
11321
|
-
0.13588013010892486e-14, -
|
|
11691
|
+
0.13588013010892486e-14, -3488903933999489e-37,
|
|
11322
11692
|
];
|
|
11323
11693
|
/**
|
|
11324
11694
|
* Polynomial coefficients for a denominator of erfInvImp
|
|
@@ -11329,6 +11699,132 @@ ErrorFunction.ervInvImpGd = [
|
|
|
11329
11699
|
0.3999688121938621e-6, 0.1618092908879045e-8, 0.2315586083102596e-11,
|
|
11330
11700
|
];
|
|
11331
11701
|
|
|
11702
|
+
/**
|
|
11703
|
+
* Continuous Univariate Normal distribution, also known as Gaussian distribution.
|
|
11704
|
+
*
|
|
11705
|
+
* For details about this distribution, see {@link http://en.wikipedia.org/wiki/Normal_distribution Wikipedia - Normal distribution}.
|
|
11706
|
+
*
|
|
11707
|
+
* This class shares the same implementation as {@link https://numerics.mathdotnet.com/ Math.NET Numerics}.
|
|
11708
|
+
*/
|
|
11709
|
+
class NormalDistribution {
|
|
11710
|
+
/**
|
|
11711
|
+
* Computes the cumulative distribution function (CDF) of the distribution at x, i.e. P(X ≤ x).
|
|
11712
|
+
*
|
|
11713
|
+
* @param mean The mean (μ) of the normal distribution.
|
|
11714
|
+
* @param stdDev The standard deviation (σ) of the normal distribution. Range: σ ≥ 0.
|
|
11715
|
+
* @param x The location at which to compute the cumulative distribution function.
|
|
11716
|
+
* @returns The cumulative distribution at {@link x}.
|
|
11717
|
+
*/
|
|
11718
|
+
static cdf(mean, stdDev, x) {
|
|
11719
|
+
if (stdDev < 0) {
|
|
11720
|
+
throw new RangeError("Invalid parametrization for the distribution.");
|
|
11721
|
+
}
|
|
11722
|
+
if (mean === x && stdDev === 0) {
|
|
11723
|
+
return 0;
|
|
11724
|
+
}
|
|
11725
|
+
return 0.5 * ErrorFunction.erfc((mean - x) / (stdDev * Math.SQRT2));
|
|
11726
|
+
}
|
|
11727
|
+
/**
|
|
11728
|
+
* Computes the probability density of the distribution (PDF) at x, i.e. ∂P(X ≤ x)/∂x.
|
|
11729
|
+
*
|
|
11730
|
+
* In MATLAB, this is known as `normpdf`.
|
|
11731
|
+
*
|
|
11732
|
+
* @param mean The mean (μ) of the normal distribution.
|
|
11733
|
+
* @param stdDev The standard deviation (σ) of the normal distribution. Range: σ ≥ 0.
|
|
11734
|
+
* @param x The location at which to compute the density.
|
|
11735
|
+
* @returns The density at {@link x}.
|
|
11736
|
+
*/
|
|
11737
|
+
static pdf(mean, stdDev, x) {
|
|
11738
|
+
if (stdDev < 0) {
|
|
11739
|
+
throw new RangeError("Invalid parametrization for the distribution.");
|
|
11740
|
+
}
|
|
11741
|
+
const d = (x - mean) / stdDev;
|
|
11742
|
+
return Math.exp(-0.5 * d * d) / (stdDev * Math.sqrt(2 * Math.PI));
|
|
11743
|
+
}
|
|
11744
|
+
/**
|
|
11745
|
+
* Computes the inverse of the cumulative distribution function (InvCDF) for the distribution
|
|
11746
|
+
* at the given probability. This is also known as the quantile or percent point function.
|
|
11747
|
+
*
|
|
11748
|
+
* @param mean The mean (μ) of the normal distribution.
|
|
11749
|
+
* @param stdDev The standard deviation (σ) of the normal distribution. Range: σ ≥ 0.
|
|
11750
|
+
* @param p The location at which to compute the inverse cumulative density.
|
|
11751
|
+
* @returns The inverse cumulative density at `p`.
|
|
11752
|
+
*/
|
|
11753
|
+
static invCDF(mean, stdDev, p) {
|
|
11754
|
+
if (stdDev < 0) {
|
|
11755
|
+
throw new RangeError("Invalid parametrization for the distribution.");
|
|
11756
|
+
}
|
|
11757
|
+
return mean - stdDev * Math.SQRT2 * ErrorFunction.erfcInv(2 * p);
|
|
11758
|
+
}
|
|
11759
|
+
}
|
|
11760
|
+
|
|
11761
|
+
/**
|
|
11762
|
+
* Approximation of the {@link https://en.wikipedia.org/wiki/Poisson_binomial_distribution Poisson binomial distribution}, which can be
|
|
11763
|
+
* updated iteratively.
|
|
11764
|
+
*
|
|
11765
|
+
* For the approximation method, see "Refined Normal Approximation (RNA)" from:
|
|
11766
|
+
*
|
|
11767
|
+
* {@link https://www.researchgate.net/publication/257017356_On_computing_the_distribution_function_for_the_Poisson_binomial_distribution Hong, Y. (2013). On computing the distribution function for the Poisson binomial distribution. Computational Statistics and Data Analysis, Vol. 59, pp. 41-51}.
|
|
11768
|
+
*
|
|
11769
|
+
* This has been verified against a reference implementation provided by the authors in the R package "poibin",
|
|
11770
|
+
* which can be viewed {@link https://rdrr.io/cran/poibin/man/poibin-package.html here}.
|
|
11771
|
+
*/
|
|
11772
|
+
class IterativePoissonBinomial {
|
|
11773
|
+
constructor() {
|
|
11774
|
+
this.mu = 0;
|
|
11775
|
+
this.var = 0;
|
|
11776
|
+
this.gamma = 0;
|
|
11777
|
+
}
|
|
11778
|
+
/**
|
|
11779
|
+
* Adds a new trial with the provided probability of success to the distribution.
|
|
11780
|
+
*
|
|
11781
|
+
* @param p The probability of success for the new trial.
|
|
11782
|
+
*/
|
|
11783
|
+
addProbability(p) {
|
|
11784
|
+
this.mu += p;
|
|
11785
|
+
this.var += p * (1 - p);
|
|
11786
|
+
this.gamma += p * (1 - p) * (1 - 2 * p);
|
|
11787
|
+
}
|
|
11788
|
+
/**
|
|
11789
|
+
* Adds multiple trials with the same probability of success to the distribution.
|
|
11790
|
+
*
|
|
11791
|
+
* @param p The probability of success for the new trials.
|
|
11792
|
+
* @param count The number of trials to add.
|
|
11793
|
+
*/
|
|
11794
|
+
addBinnedProbabilities(p, count) {
|
|
11795
|
+
this.mu += p * count;
|
|
11796
|
+
this.var += p * (1 - p) * count;
|
|
11797
|
+
this.gamma += p * (1 - p) * (1 - 2 * p) * count;
|
|
11798
|
+
}
|
|
11799
|
+
/**
|
|
11800
|
+
* Computes the value of the cumulative distribution function for this distribution.
|
|
11801
|
+
*
|
|
11802
|
+
* @param count The argument of the CDF to sample the distribution for. In the discrete case (when it is a whole number),
|
|
11803
|
+
* this corresponds to the number of successful Bernoulli trials to query the CDF for.
|
|
11804
|
+
* @returns The value of CDF at {@link count}. In the discrete case, this corresponds to the probability that at most
|
|
11805
|
+
* {@link count} Bernoulli trials ended in a success.
|
|
11806
|
+
*/
|
|
11807
|
+
cdf(count) {
|
|
11808
|
+
if (this.var === 0) {
|
|
11809
|
+
return this.mu <= count ? 1 : 0;
|
|
11810
|
+
}
|
|
11811
|
+
const sigma = Math.sqrt(this.var);
|
|
11812
|
+
const v = this.gamma / (6 * Math.pow(sigma, 3));
|
|
11813
|
+
const k = (count + 0.5 - this.mu) / sigma;
|
|
11814
|
+
const result = NormalDistribution.cdf(0, 1, k) +
|
|
11815
|
+
v * (1 - k * k) * NormalDistribution.pdf(0, 1, k);
|
|
11816
|
+
return MathUtils.clamp(result, 0, 1);
|
|
11817
|
+
}
|
|
11818
|
+
/**
|
|
11819
|
+
* Resets the distribution to an empty state.
|
|
11820
|
+
*/
|
|
11821
|
+
reset() {
|
|
11822
|
+
this.mu = 0;
|
|
11823
|
+
this.var = 0;
|
|
11824
|
+
this.gamma = 0;
|
|
11825
|
+
}
|
|
11826
|
+
}
|
|
11827
|
+
|
|
11332
11828
|
/**
|
|
11333
11829
|
* Ranking status of a beatmap.
|
|
11334
11830
|
*/
|
|
@@ -11553,13 +12049,13 @@ class MapInfo {
|
|
|
11553
12049
|
* The osu! site link to this beatmap.
|
|
11554
12050
|
*/
|
|
11555
12051
|
get beatmapLink() {
|
|
11556
|
-
return `https://osu.ppy.sh/b/${this.beatmapId}`;
|
|
12052
|
+
return `https://osu.ppy.sh/b/${this.beatmapId.toString()}`;
|
|
11557
12053
|
}
|
|
11558
12054
|
/**
|
|
11559
12055
|
* The osu! site link to this beatmapset.
|
|
11560
12056
|
*/
|
|
11561
12057
|
get beatmapSetLink() {
|
|
11562
|
-
return `https://osu.ppy.sh/s/${this.beatmapSetId}`;
|
|
12058
|
+
return `https://osu.ppy.sh/s/${this.beatmapSetId.toString()}`;
|
|
11563
12059
|
}
|
|
11564
12060
|
static getInformation(beatmapIdOrHash, downloadBeatmap) {
|
|
11565
12061
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -11570,7 +12066,7 @@ class MapInfo {
|
|
|
11570
12066
|
if (result.statusCode !== 200) {
|
|
11571
12067
|
throw new Error("osu! API error");
|
|
11572
12068
|
}
|
|
11573
|
-
const mapinfo = JSON.parse(result.data.toString("utf-8"))
|
|
12069
|
+
const mapinfo = JSON.parse(result.data.toString("utf-8")).at(0);
|
|
11574
12070
|
if (!mapinfo) {
|
|
11575
12071
|
return null;
|
|
11576
12072
|
}
|
|
@@ -11655,7 +12151,7 @@ class MapInfo {
|
|
|
11655
12151
|
toAPIResponse() {
|
|
11656
12152
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
11657
12153
|
const padDateNumber = (num) => num.toString().padStart(2, "0");
|
|
11658
|
-
const convertDate = (date) => `${date.getUTCFullYear()}-${padDateNumber(date.getUTCMonth() + 1)}-${padDateNumber(date.getUTCDate())} ${padDateNumber(date.getUTCHours())}:${padDateNumber(date.getUTCMinutes())}:${padDateNumber(date.getUTCSeconds())}`;
|
|
12154
|
+
const convertDate = (date) => `${date.getUTCFullYear().toString()}-${padDateNumber(date.getUTCMonth() + 1)}-${padDateNumber(date.getUTCDate())} ${padDateNumber(date.getUTCHours())}:${padDateNumber(date.getUTCMinutes())}:${padDateNumber(date.getUTCSeconds())}`;
|
|
11659
12155
|
return {
|
|
11660
12156
|
approved: this.approved.toString(),
|
|
11661
12157
|
submit_date: convertDate(this.submitDate),
|
|
@@ -11667,7 +12163,7 @@ class MapInfo {
|
|
|
11667
12163
|
beatmap_id: this.beatmapId.toString(),
|
|
11668
12164
|
beatmapset_id: this.beatmapSetId.toString(),
|
|
11669
12165
|
bpm: this.bpm.toString(),
|
|
11670
|
-
creator: this.creator
|
|
12166
|
+
creator: this.creator,
|
|
11671
12167
|
creator_id: this.creatorId.toString(),
|
|
11672
12168
|
difficultyrating: (_b = (_a = this.totalDifficulty) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : null,
|
|
11673
12169
|
diff_aim: (_d = (_c = this.aimDifficulty) === null || _c === void 0 ? void 0 : _c.toString()) !== null && _d !== void 0 ? _d : null,
|
|
@@ -11677,13 +12173,13 @@ class MapInfo {
|
|
|
11677
12173
|
diff_approach: this.ar.toString(),
|
|
11678
12174
|
diff_drain: this.hp.toString(),
|
|
11679
12175
|
hit_length: this.hitLength.toString(),
|
|
11680
|
-
source: this.source
|
|
12176
|
+
source: this.source,
|
|
11681
12177
|
genre_id: this.genre.toString(),
|
|
11682
12178
|
language_id: this.language.toString(),
|
|
11683
|
-
title: this.title
|
|
12179
|
+
title: this.title,
|
|
11684
12180
|
total_length: this.totalLength.toString(),
|
|
11685
|
-
version: this.version
|
|
11686
|
-
file_md5: this.hash
|
|
12181
|
+
version: this.version,
|
|
12182
|
+
file_md5: this.hash,
|
|
11687
12183
|
// Guaranteed to be osu!standard for the time being.
|
|
11688
12184
|
mode: "0",
|
|
11689
12185
|
tags: this.tags.join(" "),
|
|
@@ -11720,7 +12216,7 @@ class MapInfo {
|
|
|
11720
12216
|
if (this.hasDownloadedBeatmap() && !force) {
|
|
11721
12217
|
return;
|
|
11722
12218
|
}
|
|
11723
|
-
const url = `https://osu.ppy.sh/osu/${this.beatmapId}`;
|
|
12219
|
+
const url = `https://osu.ppy.sh/osu/${this.beatmapId.toString()}`;
|
|
11724
12220
|
return fetch(url)
|
|
11725
12221
|
.then((res) => __awaiter(this, void 0, void 0, function* () {
|
|
11726
12222
|
const text = yield res.text();
|
|
@@ -11730,7 +12226,7 @@ class MapInfo {
|
|
|
11730
12226
|
this.cachedBeatmap = new BeatmapDecoder().decode(text).result;
|
|
11731
12227
|
}))
|
|
11732
12228
|
.catch((e) => {
|
|
11733
|
-
console.error(`Request to ${url} failed with
|
|
12229
|
+
console.error(`Request to ${url} failed with an error, aborting`, e);
|
|
11734
12230
|
});
|
|
11735
12231
|
});
|
|
11736
12232
|
}
|
|
@@ -11748,7 +12244,8 @@ class MapInfo {
|
|
|
11748
12244
|
* Returns a string representative of the class.
|
|
11749
12245
|
*/
|
|
11750
12246
|
toString() {
|
|
11751
|
-
|
|
12247
|
+
var _a, _b;
|
|
12248
|
+
return `${this.fullTitle}\nCS: ${this.cs.toString()} - AR: ${this.ar.toString()} - OD: ${this.od.toString()} - HP: ${this.hp.toString()}\nBPM: ${this.bpm.toString()} - Length: ${this.hitLength.toString()}/${this.totalLength.toString()} - Max Combo: ${(_b = (_a = this.maxCombo) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : "N/A"}\nLast Update: ${this.lastUpdate.toUTCString()}`;
|
|
11752
12249
|
}
|
|
11753
12250
|
}
|
|
11754
12251
|
|
|
@@ -11787,27 +12284,62 @@ class ModOldNightCore extends ModNightCore {
|
|
|
11787
12284
|
}
|
|
11788
12285
|
|
|
11789
12286
|
/**
|
|
11790
|
-
*
|
|
12287
|
+
* Approximation of the {@link https://en.wikipedia.org/wiki/Poisson_binomial_distribution Poisson binomial distribution}.
|
|
11791
12288
|
*
|
|
11792
|
-
* For
|
|
12289
|
+
* For the approximation method, see "Refined Normal Approximation (RNA)" from:
|
|
11793
12290
|
*
|
|
11794
|
-
*
|
|
12291
|
+
* {@link https://www.researchgate.net/publication/257017356_On_computing_the_distribution_function_for_the_Poisson_binomial_distribution Hong, Y. (2013). On computing the distribution function for the Poisson binomial distribution. Computational Statistics and Data Analysis, Vol. 59, pp. 41-51}.
|
|
12292
|
+
*
|
|
12293
|
+
* This has been verified against a reference implementation provided by the authors in the R package "poibin",
|
|
12294
|
+
* which can be viewed {@link https://rdrr.io/cran/poibin/man/poibin-package.html here}.
|
|
11795
12295
|
*/
|
|
11796
|
-
class
|
|
12296
|
+
class PoissonBinomial {
|
|
11797
12297
|
/**
|
|
11798
|
-
*
|
|
11799
|
-
*
|
|
12298
|
+
* Creates a new Poisson binomial distribution based on N trials with the provided list or bins of
|
|
12299
|
+
* difficulties, skill, and method for getting the miss probabilities.
|
|
11800
12300
|
*
|
|
11801
|
-
* @param
|
|
11802
|
-
* @param
|
|
11803
|
-
* @param
|
|
11804
|
-
* @returns The inverse cumulative density at `p`.
|
|
12301
|
+
* @param difficulties The list or bins of difficulties.
|
|
12302
|
+
* @param skill The skill level to get the miss probabilities with.
|
|
12303
|
+
* @param hitProbability The method for converting difficulties and skill into miss probabilities.
|
|
11805
12304
|
*/
|
|
11806
|
-
|
|
11807
|
-
|
|
11808
|
-
|
|
12305
|
+
constructor(difficulties, skill, hitProbability) {
|
|
12306
|
+
this.mu = 0;
|
|
12307
|
+
let variance = 0;
|
|
12308
|
+
let gamma = 0;
|
|
12309
|
+
for (const d of difficulties) {
|
|
12310
|
+
if (d instanceof Bin) {
|
|
12311
|
+
const p = 1 - hitProbability(skill, d.difficulty);
|
|
12312
|
+
this.mu += p * d.noteCount;
|
|
12313
|
+
variance += p * (1 - p) * d.noteCount;
|
|
12314
|
+
gamma += p * (1 - p) * (1 - 2 * p) * d.noteCount;
|
|
12315
|
+
}
|
|
12316
|
+
else {
|
|
12317
|
+
const p = 1 - hitProbability(skill, d);
|
|
12318
|
+
this.mu += p;
|
|
12319
|
+
variance += p * (1 - p);
|
|
12320
|
+
gamma += p * (1 - p) * (1 - 2 * p);
|
|
12321
|
+
}
|
|
11809
12322
|
}
|
|
11810
|
-
|
|
12323
|
+
this.sigma = Math.sqrt(variance);
|
|
12324
|
+
this.v = gamma / (6 * Math.pow(this.sigma, 3));
|
|
12325
|
+
}
|
|
12326
|
+
/**
|
|
12327
|
+
* Computes the value of the cumulative distribution function for this distribution.
|
|
12328
|
+
*
|
|
12329
|
+
* @param count The argument of the CDF to sample the distribution for. In the discrete case (when it is a whole number),
|
|
12330
|
+
* this corresponds to the number of successful Bernoulli trials to query the CDF for.
|
|
12331
|
+
* @returns The value of CDF at {@link count}. In the discrete case, this corresponds to the probability that at most
|
|
12332
|
+
* {@link count} Bernoulli trials ended in a success.
|
|
12333
|
+
*/
|
|
12334
|
+
cdf(count) {
|
|
12335
|
+
if (this.sigma === 0) {
|
|
12336
|
+
return 1;
|
|
12337
|
+
}
|
|
12338
|
+
const k = (count + 0.5 - this.mu) / this.sigma;
|
|
12339
|
+
// See equation (14) of the cited paper.
|
|
12340
|
+
const result = NormalDistribution.cdf(0, 1, k) +
|
|
12341
|
+
this.v * (1 - k * k) * NormalDistribution.pdf(0, 1, k);
|
|
12342
|
+
return MathUtils.clamp(result, 0, 1);
|
|
11811
12343
|
}
|
|
11812
12344
|
}
|
|
11813
12345
|
|
|
@@ -11828,10 +12360,12 @@ exports.BeatmapHitObjects = BeatmapHitObjects;
|
|
|
11828
12360
|
exports.BeatmapMetadata = BeatmapMetadata;
|
|
11829
12361
|
exports.BeatmapProcessor = BeatmapProcessor;
|
|
11830
12362
|
exports.BeatmapVideo = BeatmapVideo;
|
|
12363
|
+
exports.Bin = Bin;
|
|
11831
12364
|
exports.BlendingParameters = BlendingParameters;
|
|
11832
12365
|
exports.BooleanModSetting = BooleanModSetting;
|
|
11833
12366
|
exports.BreakPoint = BreakPoint;
|
|
11834
12367
|
exports.Brent = Brent;
|
|
12368
|
+
exports.Chandrupatla = Chandrupatla;
|
|
11835
12369
|
exports.Circle = Circle;
|
|
11836
12370
|
exports.CircleSizeCalculator = CircleSizeCalculator;
|
|
11837
12371
|
exports.Command = Command;
|
|
@@ -11858,6 +12392,7 @@ exports.HitSampleInfo = HitSampleInfo;
|
|
|
11858
12392
|
exports.HitWindow = HitWindow;
|
|
11859
12393
|
exports.IntegerModSetting = IntegerModSetting;
|
|
11860
12394
|
exports.Interpolation = Interpolation;
|
|
12395
|
+
exports.IterativePoissonBinomial = IterativePoissonBinomial;
|
|
11861
12396
|
exports.MapInfo = MapInfo;
|
|
11862
12397
|
exports.MathUtils = MathUtils;
|
|
11863
12398
|
exports.Mod = Mod;
|
|
@@ -11913,6 +12448,7 @@ exports.OsuPlayableBeatmap = OsuPlayableBeatmap;
|
|
|
11913
12448
|
exports.PathApproximator = PathApproximator;
|
|
11914
12449
|
exports.PlayableBeatmap = PlayableBeatmap;
|
|
11915
12450
|
exports.Playfield = Playfield;
|
|
12451
|
+
exports.PoissonBinomial = PoissonBinomial;
|
|
11916
12452
|
exports.Polynomial = Polynomial;
|
|
11917
12453
|
exports.PreciseDroidHitWindow = PreciseDroidHitWindow;
|
|
11918
12454
|
exports.Precision = Precision;
|