@rian8337/osu-base 4.0.0-beta.23 → 4.0.0-beta.25
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 +597 -445
- package/package.json +2 -2
- package/typings/index.d.ts +80 -37
package/dist/index.js
CHANGED
|
@@ -1043,6 +1043,30 @@ function convertApproachRateMilliseconds(ms) {
|
|
|
1043
1043
|
* Represents a hitobject in a beatmap.
|
|
1044
1044
|
*/
|
|
1045
1045
|
class HitObject {
|
|
1046
|
+
/**
|
|
1047
|
+
* The position of the hitobject in osu!pixels.
|
|
1048
|
+
*/
|
|
1049
|
+
get position() {
|
|
1050
|
+
return this._position;
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* The position of the hitobject in osu!pixels.
|
|
1054
|
+
*/
|
|
1055
|
+
set position(value) {
|
|
1056
|
+
this._position = value;
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* The end position of the hitobject in osu!pixels.
|
|
1060
|
+
*/
|
|
1061
|
+
get endPosition() {
|
|
1062
|
+
return this.position;
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* The end time of the hitobject.
|
|
1066
|
+
*/
|
|
1067
|
+
get endTime() {
|
|
1068
|
+
return this.startTime;
|
|
1069
|
+
}
|
|
1046
1070
|
/**
|
|
1047
1071
|
* The duration of the hitobject.
|
|
1048
1072
|
*/
|
|
@@ -1090,13 +1114,13 @@ class HitObject {
|
|
|
1090
1114
|
return res.substring(0, Math.max(0, res.length - 3));
|
|
1091
1115
|
}
|
|
1092
1116
|
/**
|
|
1093
|
-
* The radius of this hitobject
|
|
1117
|
+
* The radius of this hitobject in osu!pixels.
|
|
1094
1118
|
*/
|
|
1095
1119
|
get radius() {
|
|
1096
|
-
return HitObject.baseRadius * this.
|
|
1120
|
+
return HitObject.baseRadius * this.scale;
|
|
1097
1121
|
}
|
|
1098
1122
|
constructor(values) {
|
|
1099
|
-
var _a, _b, _c
|
|
1123
|
+
var _a, _b, _c;
|
|
1100
1124
|
/**
|
|
1101
1125
|
* The samples to be played when this hitobject is hit.
|
|
1102
1126
|
*
|
|
@@ -1125,12 +1149,10 @@ class HitObject {
|
|
|
1125
1149
|
*/
|
|
1126
1150
|
this.timeFadeIn = 400;
|
|
1127
1151
|
this.startTime = values.startTime;
|
|
1128
|
-
this.
|
|
1129
|
-
this.
|
|
1130
|
-
this.
|
|
1131
|
-
this.
|
|
1132
|
-
this.isNewCombo = (_d = values.newCombo) !== null && _d !== void 0 ? _d : false;
|
|
1133
|
-
this.comboOffset = (_e = values.comboOffset) !== null && _e !== void 0 ? _e : 0;
|
|
1152
|
+
this.type = (_a = values.type) !== null && _a !== void 0 ? _a : exports.ObjectTypes.circle;
|
|
1153
|
+
this._position = values.position;
|
|
1154
|
+
this.isNewCombo = (_b = values.newCombo) !== null && _b !== void 0 ? _b : false;
|
|
1155
|
+
this.comboOffset = (_c = values.comboOffset) !== null && _c !== void 0 ? _c : 0;
|
|
1134
1156
|
}
|
|
1135
1157
|
/**
|
|
1136
1158
|
* Applies default values to this hitobject.
|
|
@@ -1151,11 +1173,11 @@ class HitObject {
|
|
|
1151
1173
|
const cs = calculateDroidDifficultyStatistics({
|
|
1152
1174
|
circleSize: difficulty.cs,
|
|
1153
1175
|
}).circleSize;
|
|
1154
|
-
this.
|
|
1176
|
+
this.scale = CircleSizeCalculator.standardCSToStandardScale(cs, true);
|
|
1155
1177
|
break;
|
|
1156
1178
|
}
|
|
1157
1179
|
case exports.Modes.osu:
|
|
1158
|
-
this.
|
|
1180
|
+
this.scale = CircleSizeCalculator.standardCSToStandardScale(difficulty.cs, true);
|
|
1159
1181
|
break;
|
|
1160
1182
|
}
|
|
1161
1183
|
}
|
|
@@ -1179,9 +1201,11 @@ class HitObject {
|
|
|
1179
1201
|
getStackOffset(mode) {
|
|
1180
1202
|
switch (mode) {
|
|
1181
1203
|
case exports.Modes.droid:
|
|
1182
|
-
return new Vector2(this.
|
|
1204
|
+
return new Vector2(this.stackHeight *
|
|
1205
|
+
CircleSizeCalculator.standardScaleToDroidScale(this.scale, true) *
|
|
1206
|
+
4);
|
|
1183
1207
|
case exports.Modes.osu:
|
|
1184
|
-
return new Vector2(this.
|
|
1208
|
+
return new Vector2(this.stackHeight * this.scale * -6.4);
|
|
1185
1209
|
}
|
|
1186
1210
|
}
|
|
1187
1211
|
/**
|
|
@@ -1252,7 +1276,7 @@ class SliderNestedHitObject extends HitObject {
|
|
|
1252
1276
|
this.spanStartTime = values.spanStartTime;
|
|
1253
1277
|
}
|
|
1254
1278
|
toString() {
|
|
1255
|
-
return `Position: [${this.
|
|
1279
|
+
return `Position: [${this._position.x}, ${this._position.y}], span index: ${this.spanIndex}, span start time: ${this.spanStartTime}`;
|
|
1256
1280
|
}
|
|
1257
1281
|
}
|
|
1258
1282
|
|
|
@@ -1283,10 +1307,83 @@ class SliderTick extends SliderNestedHitObject {
|
|
|
1283
1307
|
class SliderTail extends SliderNestedHitObject {
|
|
1284
1308
|
}
|
|
1285
1309
|
|
|
1310
|
+
/**
|
|
1311
|
+
* Describes a value that can be cached.
|
|
1312
|
+
*/
|
|
1313
|
+
class Cached {
|
|
1314
|
+
/**
|
|
1315
|
+
* The cached value.
|
|
1316
|
+
*/
|
|
1317
|
+
get value() {
|
|
1318
|
+
if (!this._isValid) {
|
|
1319
|
+
throw new Error("May not query value of an invalid cache.");
|
|
1320
|
+
}
|
|
1321
|
+
return this._value;
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* The cached value.
|
|
1325
|
+
*/
|
|
1326
|
+
set value(value) {
|
|
1327
|
+
this._value = value;
|
|
1328
|
+
this._isValid = true;
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Whether the cache is valid.
|
|
1332
|
+
*/
|
|
1333
|
+
get isValid() {
|
|
1334
|
+
return this._isValid;
|
|
1335
|
+
}
|
|
1336
|
+
constructor(value) {
|
|
1337
|
+
this._isValid = true;
|
|
1338
|
+
this._value = value;
|
|
1339
|
+
}
|
|
1340
|
+
/**
|
|
1341
|
+
* Invalidates the cache of this `Cached`.
|
|
1342
|
+
*
|
|
1343
|
+
* @return `true` if the cache was invalidated from a valid state.
|
|
1344
|
+
*/
|
|
1345
|
+
invalidate() {
|
|
1346
|
+
if (this._isValid) {
|
|
1347
|
+
this._isValid = false;
|
|
1348
|
+
return true;
|
|
1349
|
+
}
|
|
1350
|
+
return false;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1286
1354
|
/**
|
|
1287
1355
|
* Represents a slider in a beatmap.
|
|
1288
1356
|
*/
|
|
1289
1357
|
class Slider extends HitObject {
|
|
1358
|
+
get position() {
|
|
1359
|
+
return super.position;
|
|
1360
|
+
}
|
|
1361
|
+
set position(value) {
|
|
1362
|
+
super.position = value;
|
|
1363
|
+
this.updateNestedPositions();
|
|
1364
|
+
}
|
|
1365
|
+
get endTime() {
|
|
1366
|
+
return (this.startTime + (this.spanCount * this.distance) / this.velocity);
|
|
1367
|
+
}
|
|
1368
|
+
get endPosition() {
|
|
1369
|
+
if (!this.endPositionCache.isValid) {
|
|
1370
|
+
this.endPositionCache.value = this.position.add(this.curvePositionAt(1));
|
|
1371
|
+
}
|
|
1372
|
+
return this.endPositionCache.value;
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* The slider's path.
|
|
1376
|
+
*/
|
|
1377
|
+
get path() {
|
|
1378
|
+
return this._path;
|
|
1379
|
+
}
|
|
1380
|
+
/**
|
|
1381
|
+
* The slider's path.
|
|
1382
|
+
*/
|
|
1383
|
+
set path(value) {
|
|
1384
|
+
this._path = value;
|
|
1385
|
+
this.updateNestedPositions();
|
|
1386
|
+
}
|
|
1290
1387
|
/**
|
|
1291
1388
|
* The slider's velocity.
|
|
1292
1389
|
*/
|
|
@@ -1330,6 +1427,18 @@ class Slider extends HitObject {
|
|
|
1330
1427
|
get spanDuration() {
|
|
1331
1428
|
return this.duration / this.spanCount;
|
|
1332
1429
|
}
|
|
1430
|
+
/**
|
|
1431
|
+
* The slider's head.
|
|
1432
|
+
*/
|
|
1433
|
+
get head() {
|
|
1434
|
+
return this._head;
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* The slider's tail.
|
|
1438
|
+
*/
|
|
1439
|
+
get tail() {
|
|
1440
|
+
return this._tail;
|
|
1441
|
+
}
|
|
1333
1442
|
/**
|
|
1334
1443
|
* The amount of slider ticks in this slider.
|
|
1335
1444
|
*
|
|
@@ -1387,31 +1496,21 @@ class Slider extends HitObject {
|
|
|
1387
1496
|
* as few movements as possible. This is set and used by difficulty calculation.
|
|
1388
1497
|
*/
|
|
1389
1498
|
this.lazyTravelTime = 0;
|
|
1390
|
-
this.
|
|
1499
|
+
this._path = values.path;
|
|
1391
1500
|
this.nodeSamples = values.nodeSamples;
|
|
1392
1501
|
this._repeatCount = values.repeatCount;
|
|
1393
|
-
this.
|
|
1502
|
+
this.endPositionCache = new Cached(this.position.add(this.curvePositionAt(1)));
|
|
1394
1503
|
this.tickDistanceMultiplier = values.tickDistanceMultiplier;
|
|
1395
|
-
this.
|
|
1396
|
-
position: this.
|
|
1504
|
+
this._head = new SliderHead({
|
|
1505
|
+
position: this._position,
|
|
1397
1506
|
startTime: this.startTime,
|
|
1398
1507
|
});
|
|
1399
|
-
this.
|
|
1508
|
+
this._tail = new SliderTail({
|
|
1400
1509
|
position: this.endPosition,
|
|
1401
1510
|
startTime: this.endTime,
|
|
1402
1511
|
spanIndex: this.spanCount - 1,
|
|
1403
1512
|
spanStartTime: this.startTime + this.spanDuration * this.spanCount,
|
|
1404
1513
|
});
|
|
1405
|
-
// Create sliding samples
|
|
1406
|
-
const bankSamples = this.samples.filter((v) => v instanceof BankHitSampleInfo);
|
|
1407
|
-
const normalSample = bankSamples.find((v) => v.name === BankHitSampleInfo.HIT_NORMAL);
|
|
1408
|
-
if (normalSample) {
|
|
1409
|
-
this.auxiliarySamples.push(new BankHitSampleInfo("sliderslide", normalSample.bank, normalSample.customSampleBank, normalSample.volume, normalSample.isLayered));
|
|
1410
|
-
}
|
|
1411
|
-
const whistleSample = bankSamples.find((v) => v.name === BankHitSampleInfo.HIT_WHISTLE);
|
|
1412
|
-
if (whistleSample) {
|
|
1413
|
-
this.auxiliarySamples.push(new BankHitSampleInfo("sliderwhistle", whistleSample.bank, whistleSample.customSampleBank, whistleSample.volume, whistleSample.isLayered));
|
|
1414
|
-
}
|
|
1415
1514
|
}
|
|
1416
1515
|
applyDefaults(controlPoints, difficulty, mode) {
|
|
1417
1516
|
super.applyDefaults(controlPoints, difficulty, mode);
|
|
@@ -1426,20 +1525,28 @@ class Slider extends HitObject {
|
|
|
1426
1525
|
(timingPoint.msPerBeat * bpmMultiplier);
|
|
1427
1526
|
// WARNING: this is intentionally not computed as `BASE_SCORING_DISTANCE * difficulty.sliderMultiplier`
|
|
1428
1527
|
// for backwards compatibility reasons (intentionally introducing floating point errors to match osu!stable).
|
|
1429
|
-
const scoringDistance = this.
|
|
1528
|
+
const scoringDistance = this.velocity * timingPoint.msPerBeat;
|
|
1430
1529
|
this.generateTicks = difficultyPoint.generateTicks;
|
|
1431
1530
|
this._tickDistance = this.generateTicks
|
|
1432
1531
|
? (scoringDistance / difficulty.sliderTickRate) *
|
|
1433
1532
|
this.tickDistanceMultiplier
|
|
1434
1533
|
: Number.POSITIVE_INFINITY;
|
|
1435
|
-
this.endTime =
|
|
1436
|
-
this.startTime +
|
|
1437
|
-
(this.spanCount * this.path.expectedDistance) / this._velocity;
|
|
1438
1534
|
this.createNestedHitObjects(mode);
|
|
1439
1535
|
this.nestedHitObjects.forEach((v) => v.applyDefaults(controlPoints, difficulty, mode));
|
|
1440
1536
|
}
|
|
1441
1537
|
applySamples(controlPoints) {
|
|
1442
1538
|
super.applySamples(controlPoints);
|
|
1539
|
+
// Create sliding samples
|
|
1540
|
+
this.auxiliarySamples.length = 0;
|
|
1541
|
+
const bankSamples = this.samples.filter((v) => v instanceof BankHitSampleInfo);
|
|
1542
|
+
const normalSample = bankSamples.find((v) => v.name === BankHitSampleInfo.HIT_NORMAL);
|
|
1543
|
+
if (normalSample) {
|
|
1544
|
+
this.auxiliarySamples.push(new BankHitSampleInfo("sliderslide", normalSample.bank, normalSample.customSampleBank, normalSample.volume, normalSample.isLayered));
|
|
1545
|
+
}
|
|
1546
|
+
const whistleSample = bankSamples.find((v) => v.name === BankHitSampleInfo.HIT_WHISTLE);
|
|
1547
|
+
if (whistleSample) {
|
|
1548
|
+
this.auxiliarySamples.push(new BankHitSampleInfo("sliderwhistle", whistleSample.bank, whistleSample.customSampleBank, whistleSample.volume, whistleSample.isLayered));
|
|
1549
|
+
}
|
|
1443
1550
|
this.nodeSamples.forEach((nodeSample, i) => {
|
|
1444
1551
|
const time = this.startTime +
|
|
1445
1552
|
i * this.spanDuration +
|
|
@@ -1481,7 +1588,7 @@ class Slider extends HitObject {
|
|
|
1481
1588
|
}
|
|
1482
1589
|
createNestedHitObjects(mode) {
|
|
1483
1590
|
this.nestedHitObjects.length = 0;
|
|
1484
|
-
this.
|
|
1591
|
+
this._head = new SliderHead({
|
|
1485
1592
|
position: this.position,
|
|
1486
1593
|
startTime: this.startTime,
|
|
1487
1594
|
});
|
|
@@ -1490,9 +1597,9 @@ class Slider extends HitObject {
|
|
|
1490
1597
|
// This exists for edge cases such as /b/1573664 where the beatmap has been edited by the user, and should never be reached in normal usage.
|
|
1491
1598
|
const maxLength = 100000;
|
|
1492
1599
|
const length = Math.min(maxLength, this.path.expectedDistance);
|
|
1493
|
-
const tickDistance = MathUtils.clamp(this.
|
|
1600
|
+
const tickDistance = MathUtils.clamp(this.tickDistance, 0, length);
|
|
1494
1601
|
if (tickDistance !== 0 && this.generateTicks) {
|
|
1495
|
-
const minDistanceFromEnd = this.
|
|
1602
|
+
const minDistanceFromEnd = this.velocity * 10;
|
|
1496
1603
|
for (let span = 0; span < this.spanCount; ++span) {
|
|
1497
1604
|
const spanStartTime = this.startTime + span * this.spanDuration;
|
|
1498
1605
|
const reversed = span % 2 === 1;
|
|
@@ -1534,7 +1641,7 @@ class Slider extends HitObject {
|
|
|
1534
1641
|
}
|
|
1535
1642
|
switch (mode) {
|
|
1536
1643
|
case exports.Modes.droid:
|
|
1537
|
-
this.
|
|
1644
|
+
this._tail = new SliderTail({
|
|
1538
1645
|
position: this.endPosition,
|
|
1539
1646
|
startTime: this.endTime,
|
|
1540
1647
|
spanIndex: this.spanCount - 1,
|
|
@@ -1554,7 +1661,7 @@ class Slider extends HitObject {
|
|
|
1554
1661
|
const finalSpanEndTime = Math.max(this.startTime + this.duration / 2, finalSpanStartTime +
|
|
1555
1662
|
this.spanDuration -
|
|
1556
1663
|
Slider.legacyLastTickOffset);
|
|
1557
|
-
this.
|
|
1664
|
+
this._tail = new SliderTail({
|
|
1558
1665
|
position: this.endPosition,
|
|
1559
1666
|
startTime: finalSpanEndTime,
|
|
1560
1667
|
spanIndex: this.spanCount - 1,
|
|
@@ -1568,6 +1675,7 @@ class Slider extends HitObject {
|
|
|
1568
1675
|
this.updateNestedSamples();
|
|
1569
1676
|
}
|
|
1570
1677
|
updateNestedPositions() {
|
|
1678
|
+
this.endPositionCache.invalidate();
|
|
1571
1679
|
this.head.position = this.position;
|
|
1572
1680
|
this.tail.position = this.endPosition;
|
|
1573
1681
|
}
|
|
@@ -2253,7 +2361,7 @@ class Circle extends HitObject {
|
|
|
2253
2361
|
super(values);
|
|
2254
2362
|
}
|
|
2255
2363
|
toString() {
|
|
2256
|
-
return `Position: [${this.
|
|
2364
|
+
return `Position: [${this._position.x}, ${this._position.y}]`;
|
|
2257
2365
|
}
|
|
2258
2366
|
}
|
|
2259
2367
|
|
|
@@ -2264,8 +2372,16 @@ class Circle extends HitObject {
|
|
|
2264
2372
|
* position of a spinner is always at 256x192.
|
|
2265
2373
|
*/
|
|
2266
2374
|
class Spinner extends HitObject {
|
|
2375
|
+
get endTime() {
|
|
2376
|
+
return this._endTime;
|
|
2377
|
+
}
|
|
2267
2378
|
constructor(values) {
|
|
2268
2379
|
super(Object.assign(Object.assign({}, values), { position: new Vector2(256, 192) }));
|
|
2380
|
+
this._endTime = values.endTime;
|
|
2381
|
+
}
|
|
2382
|
+
applySamples(controlPoints) {
|
|
2383
|
+
super.applySamples(controlPoints);
|
|
2384
|
+
this.auxiliarySamples.length = 0;
|
|
2269
2385
|
const bankSample = this.samples.find((v) => v instanceof BankHitSampleInfo);
|
|
2270
2386
|
if (bankSample) {
|
|
2271
2387
|
this.auxiliarySamples.push(new BankHitSampleInfo("spinnerspin", bankSample.bank, bankSample.customSampleBank, bankSample.volume, bankSample.isLayered));
|
|
@@ -2279,7 +2395,7 @@ class Spinner extends HitObject {
|
|
|
2279
2395
|
return this.position;
|
|
2280
2396
|
}
|
|
2281
2397
|
toString() {
|
|
2282
|
-
return `Position: [${this.
|
|
2398
|
+
return `Position: [${this._position.x}, ${this._position.y}], duration: ${this.duration}`;
|
|
2283
2399
|
}
|
|
2284
2400
|
}
|
|
2285
2401
|
|
|
@@ -2424,293 +2540,59 @@ class BeatmapHitObjects {
|
|
|
2424
2540
|
}
|
|
2425
2541
|
|
|
2426
2542
|
/**
|
|
2427
|
-
*
|
|
2543
|
+
* Provides functionality to alter a beatmap after it has been converted.
|
|
2428
2544
|
*/
|
|
2429
|
-
class
|
|
2430
|
-
constructor(
|
|
2431
|
-
|
|
2432
|
-
this.formatVersion = shallowCopy.formatVersion;
|
|
2433
|
-
this.general = shallowCopy.general;
|
|
2434
|
-
this.editor = shallowCopy.editor;
|
|
2435
|
-
this.metadata = shallowCopy.metadata;
|
|
2436
|
-
this.difficulty = shallowCopy.difficulty;
|
|
2437
|
-
this.events = shallowCopy.events;
|
|
2438
|
-
this.controlPoints = shallowCopy.controlPoints;
|
|
2439
|
-
this.colors = shallowCopy.colors;
|
|
2440
|
-
this.hitObjects = shallowCopy.hitObjects;
|
|
2441
|
-
return;
|
|
2442
|
-
}
|
|
2443
|
-
this.formatVersion = 1;
|
|
2444
|
-
this.general = new BeatmapGeneral();
|
|
2445
|
-
this.editor = new BeatmapEditor();
|
|
2446
|
-
this.metadata = new BeatmapMetadata();
|
|
2447
|
-
this.difficulty = new BeatmapDifficulty();
|
|
2448
|
-
this.events = new BeatmapEvents();
|
|
2449
|
-
this.controlPoints = new BeatmapControlPoints();
|
|
2450
|
-
this.colors = new BeatmapColor();
|
|
2451
|
-
this.hitObjects = new BeatmapHitObjects();
|
|
2452
|
-
}
|
|
2453
|
-
/**
|
|
2454
|
-
* The maximum combo of the beatmap.
|
|
2455
|
-
*/
|
|
2456
|
-
get maxCombo() {
|
|
2457
|
-
return (this.hitObjects.circles +
|
|
2458
|
-
this.hitObjects.sliders +
|
|
2459
|
-
this.hitObjects.sliderTicks +
|
|
2460
|
-
this.hitObjects.sliderRepeatPoints +
|
|
2461
|
-
this.hitObjects.sliderEnds +
|
|
2462
|
-
this.hitObjects.spinners);
|
|
2463
|
-
}
|
|
2464
|
-
/**
|
|
2465
|
-
* The most common beat length of the beatmap.
|
|
2466
|
-
*/
|
|
2467
|
-
get mostCommonBeatLength() {
|
|
2468
|
-
var _a, _b, _c, _d, _e;
|
|
2469
|
-
// The last playable time in the beatmap - the last timing point extends to this time.
|
|
2470
|
-
// 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.
|
|
2471
|
-
const lastTime = (_d = (_b = (_a = this.hitObjects.objects[this.hitObjects.objects.length - 1]) === null || _a === void 0 ? void 0 : _a.endTime) !== null && _b !== void 0 ? _b : (_c = this.controlPoints.timing.points[this.controlPoints.timing.points.length - 1]) === null || _c === void 0 ? void 0 : _c.time) !== null && _d !== void 0 ? _d : 0;
|
|
2472
|
-
const mostCommon =
|
|
2473
|
-
// Construct a set of {beatLength, duration} objects for each individual timing point.
|
|
2474
|
-
this.controlPoints.timing.points
|
|
2475
|
-
.map((t, i, a) => {
|
|
2476
|
-
if (t.time > lastTime) {
|
|
2477
|
-
return { beatLength: t.msPerBeat, duration: 0 };
|
|
2478
|
-
}
|
|
2479
|
-
// osu-stable forced the first control point to start at 0.
|
|
2480
|
-
const currentTime = i === 0 ? 0 : t.time;
|
|
2481
|
-
const nextTime = i === a.length - 1 ? lastTime : a[i + 1].time;
|
|
2482
|
-
return {
|
|
2483
|
-
beatLength: t.msPerBeat,
|
|
2484
|
-
duration: nextTime - currentTime,
|
|
2485
|
-
};
|
|
2486
|
-
})
|
|
2487
|
-
// Get the most common one, or 0 as a suitable default.
|
|
2488
|
-
.sort((a, b) => b.duration - a.duration)[0];
|
|
2489
|
-
return (_e = mostCommon === null || mostCommon === void 0 ? void 0 : mostCommon.beatLength) !== null && _e !== void 0 ? _e : 0;
|
|
2545
|
+
class BeatmapProcessor {
|
|
2546
|
+
constructor(beatmap) {
|
|
2547
|
+
this.beatmap = beatmap;
|
|
2490
2548
|
}
|
|
2491
2549
|
/**
|
|
2492
|
-
*
|
|
2550
|
+
* Processes the converted beatmap after `HitObject.applyDefaults` has been invoked.
|
|
2493
2551
|
*
|
|
2494
|
-
*
|
|
2552
|
+
* Nested hitobjects generated during `HitObject.applyDefaults` wil be present by this point,
|
|
2553
|
+
* and mods will have been applied to all hitobjects.
|
|
2495
2554
|
*
|
|
2496
|
-
*
|
|
2497
|
-
*/
|
|
2498
|
-
getOffsetTime(time) {
|
|
2499
|
-
return time + (this.formatVersion < 5 ? 24 : 0);
|
|
2500
|
-
}
|
|
2501
|
-
/**
|
|
2502
|
-
* Calculates the osu!droid maximum score of the beatmap without taking spinner bonus into account.
|
|
2555
|
+
* This should be used to add alterations to hitobjects while they are in their most playable state.
|
|
2503
2556
|
*
|
|
2504
|
-
* @param
|
|
2505
|
-
* @param customSpeedMultiplier The custom speed multiplier of the beatmap. Defaults to 1.
|
|
2557
|
+
* @param mode The mode to add alterations for.
|
|
2506
2558
|
*/
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
scoreMultiplier *= mod.droidScoreMultiplier;
|
|
2512
|
-
}
|
|
2559
|
+
postProcess(mode) {
|
|
2560
|
+
const objects = this.beatmap.hitObjects.objects;
|
|
2561
|
+
if (objects.length === 0) {
|
|
2562
|
+
return;
|
|
2513
2563
|
}
|
|
2514
|
-
|
|
2515
|
-
|
|
2564
|
+
// Reset stacking
|
|
2565
|
+
objects.forEach((h) => {
|
|
2566
|
+
h.stackHeight = 0;
|
|
2567
|
+
});
|
|
2568
|
+
switch (mode) {
|
|
2569
|
+
case exports.Modes.droid:
|
|
2570
|
+
this.applyDroidStacking();
|
|
2571
|
+
break;
|
|
2572
|
+
case exports.Modes.osu:
|
|
2573
|
+
if (this.beatmap.formatVersion >= 6) {
|
|
2574
|
+
this.applyStandardStacking();
|
|
2575
|
+
}
|
|
2576
|
+
else {
|
|
2577
|
+
this.applyStandardOldStacking();
|
|
2578
|
+
}
|
|
2579
|
+
break;
|
|
2516
2580
|
}
|
|
2517
|
-
|
|
2518
|
-
|
|
2581
|
+
}
|
|
2582
|
+
applyDroidStacking() {
|
|
2583
|
+
const objects = this.beatmap.hitObjects.objects;
|
|
2584
|
+
if (objects.length === 0) {
|
|
2585
|
+
return;
|
|
2519
2586
|
}
|
|
2520
|
-
const
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
++combo;
|
|
2530
|
-
continue;
|
|
2531
|
-
}
|
|
2532
|
-
const { ticks } = object;
|
|
2533
|
-
// Apply slider head.
|
|
2534
|
-
score += 30;
|
|
2535
|
-
++combo;
|
|
2536
|
-
// Apply slider repeats.
|
|
2537
|
-
score += 30 * object.repeatCount;
|
|
2538
|
-
combo += object.repeatCount;
|
|
2539
|
-
// Apply slider ticks.
|
|
2540
|
-
score += 10 * ticks;
|
|
2541
|
-
combo += ticks;
|
|
2542
|
-
// Apply slider end.
|
|
2543
|
-
score += Math.floor(300 + (300 * combo * difficultyMultiplier) / 25);
|
|
2544
|
-
++combo;
|
|
2545
|
-
}
|
|
2546
|
-
return Math.floor(score * scoreMultiplier);
|
|
2547
|
-
}
|
|
2548
|
-
/**
|
|
2549
|
-
* Calculates the osu!standard maximum score of the beatmap without taking spinner bonus into account.
|
|
2550
|
-
*
|
|
2551
|
-
* @param mods The modifications to calculate for. Defaults to No Mod.
|
|
2552
|
-
*/
|
|
2553
|
-
maxOsuScore(mods = []) {
|
|
2554
|
-
const accumulatedDiffPoints = this.difficulty.cs + this.difficulty.hp + this.difficulty.od;
|
|
2555
|
-
let difficultyMultiplier = 2;
|
|
2556
|
-
let scoreMultiplier = 1;
|
|
2557
|
-
for (const mod of mods) {
|
|
2558
|
-
if (mod.isApplicableToOsu()) {
|
|
2559
|
-
scoreMultiplier *= mod.pcScoreMultiplier;
|
|
2560
|
-
}
|
|
2561
|
-
}
|
|
2562
|
-
switch (true) {
|
|
2563
|
-
case accumulatedDiffPoints <= 5:
|
|
2564
|
-
difficultyMultiplier = 2;
|
|
2565
|
-
break;
|
|
2566
|
-
case accumulatedDiffPoints <= 12:
|
|
2567
|
-
difficultyMultiplier = 3;
|
|
2568
|
-
break;
|
|
2569
|
-
case accumulatedDiffPoints <= 17:
|
|
2570
|
-
difficultyMultiplier = 4;
|
|
2571
|
-
break;
|
|
2572
|
-
case accumulatedDiffPoints <= 24:
|
|
2573
|
-
difficultyMultiplier = 5;
|
|
2574
|
-
break;
|
|
2575
|
-
case accumulatedDiffPoints >= 25:
|
|
2576
|
-
difficultyMultiplier = 6;
|
|
2577
|
-
break;
|
|
2578
|
-
}
|
|
2579
|
-
let combo = 0;
|
|
2580
|
-
let score = 0;
|
|
2581
|
-
for (const object of this.hitObjects.objects) {
|
|
2582
|
-
if (!(object instanceof Slider)) {
|
|
2583
|
-
score += Math.floor(300 +
|
|
2584
|
-
(300 * combo * difficultyMultiplier * scoreMultiplier) /
|
|
2585
|
-
25);
|
|
2586
|
-
++combo;
|
|
2587
|
-
continue;
|
|
2588
|
-
}
|
|
2589
|
-
const { ticks } = object;
|
|
2590
|
-
// Apply slider head.
|
|
2591
|
-
score += 30;
|
|
2592
|
-
++combo;
|
|
2593
|
-
// Apply slider repeats.
|
|
2594
|
-
score += 30 * object.repeatCount;
|
|
2595
|
-
combo += object.repeatCount;
|
|
2596
|
-
// Apply slider ticks.
|
|
2597
|
-
score += 10 * ticks;
|
|
2598
|
-
combo += ticks;
|
|
2599
|
-
// Apply slider end.
|
|
2600
|
-
score += Math.floor(300 +
|
|
2601
|
-
(300 * combo * difficultyMultiplier * scoreMultiplier) / 25);
|
|
2602
|
-
++combo;
|
|
2603
|
-
}
|
|
2604
|
-
return score;
|
|
2605
|
-
}
|
|
2606
|
-
/**
|
|
2607
|
-
* Returns a string representative of the class.
|
|
2608
|
-
*/
|
|
2609
|
-
toString() {
|
|
2610
|
-
let res = this.metadata.artist + " - " + this.metadata.title + " [";
|
|
2611
|
-
if (this.metadata.titleUnicode || this.metadata.artistUnicode) {
|
|
2612
|
-
res +=
|
|
2613
|
-
"(" +
|
|
2614
|
-
this.metadata.artistUnicode +
|
|
2615
|
-
" - " +
|
|
2616
|
-
this.metadata.titleUnicode +
|
|
2617
|
-
")";
|
|
2618
|
-
}
|
|
2619
|
-
res +=
|
|
2620
|
-
this.metadata.version +
|
|
2621
|
-
"] mapped by " +
|
|
2622
|
-
this.metadata.creator +
|
|
2623
|
-
"\n" +
|
|
2624
|
-
"\n" +
|
|
2625
|
-
"AR" +
|
|
2626
|
-
MathUtils.round(this.difficulty.ar, 2) +
|
|
2627
|
-
" " +
|
|
2628
|
-
"OD" +
|
|
2629
|
-
MathUtils.round(this.difficulty.od, 2) +
|
|
2630
|
-
" " +
|
|
2631
|
-
"CS" +
|
|
2632
|
-
MathUtils.round(this.difficulty.cs, 2) +
|
|
2633
|
-
" " +
|
|
2634
|
-
"HP" +
|
|
2635
|
-
MathUtils.round(this.difficulty.hp, 2) +
|
|
2636
|
-
"\n" +
|
|
2637
|
-
this.hitObjects.circles +
|
|
2638
|
-
" circles, " +
|
|
2639
|
-
this.hitObjects.sliders +
|
|
2640
|
-
" sliders, " +
|
|
2641
|
-
this.hitObjects.spinners +
|
|
2642
|
-
" spinners" +
|
|
2643
|
-
"\n" +
|
|
2644
|
-
this.maxCombo +
|
|
2645
|
-
" max combo";
|
|
2646
|
-
return res;
|
|
2647
|
-
}
|
|
2648
|
-
}
|
|
2649
|
-
|
|
2650
|
-
/**
|
|
2651
|
-
* Represents a beatmap's background.
|
|
2652
|
-
*/
|
|
2653
|
-
class BeatmapBackground {
|
|
2654
|
-
constructor(filename, offset) {
|
|
2655
|
-
this.filename = filename;
|
|
2656
|
-
this.offset = offset;
|
|
2657
|
-
}
|
|
2658
|
-
}
|
|
2659
|
-
|
|
2660
|
-
/**
|
|
2661
|
-
* Provides functionality to alter a beatmap after it has been converted.
|
|
2662
|
-
*/
|
|
2663
|
-
class BeatmapProcessor {
|
|
2664
|
-
constructor(beatmap) {
|
|
2665
|
-
this.beatmap = beatmap;
|
|
2666
|
-
}
|
|
2667
|
-
/**
|
|
2668
|
-
* Processes the converted beatmap after `HitObject.applyDefaults` has been invoked.
|
|
2669
|
-
*
|
|
2670
|
-
* Nested hitobjects generated during `HitObject.applyDefaults` wil be present by this point,
|
|
2671
|
-
* and mods will have been applied to all hitobjects.
|
|
2672
|
-
*
|
|
2673
|
-
* This should be used to add alterations to hitobjects while they are in their most playable state.
|
|
2674
|
-
*
|
|
2675
|
-
* @param mode The mode to add alterations for.
|
|
2676
|
-
*/
|
|
2677
|
-
postProcess(mode) {
|
|
2678
|
-
const objects = this.beatmap.hitObjects.objects;
|
|
2679
|
-
if (objects.length === 0) {
|
|
2680
|
-
return;
|
|
2681
|
-
}
|
|
2682
|
-
// Reset stacking
|
|
2683
|
-
objects.forEach((h) => {
|
|
2684
|
-
h.stackHeight = 0;
|
|
2685
|
-
});
|
|
2686
|
-
switch (mode) {
|
|
2687
|
-
case exports.Modes.droid:
|
|
2688
|
-
this.applyDroidStacking();
|
|
2689
|
-
break;
|
|
2690
|
-
case exports.Modes.osu:
|
|
2691
|
-
if (this.beatmap.formatVersion >= 6) {
|
|
2692
|
-
this.applyStandardStacking();
|
|
2693
|
-
}
|
|
2694
|
-
else {
|
|
2695
|
-
this.applyStandardOldStacking();
|
|
2696
|
-
}
|
|
2697
|
-
break;
|
|
2698
|
-
}
|
|
2699
|
-
}
|
|
2700
|
-
applyDroidStacking() {
|
|
2701
|
-
const objects = this.beatmap.hitObjects.objects;
|
|
2702
|
-
if (objects.length === 0) {
|
|
2703
|
-
return;
|
|
2704
|
-
}
|
|
2705
|
-
const convertedScale = CircleSizeCalculator.standardScaleToDroidScale(objects[0].scale);
|
|
2706
|
-
for (let i = 0; i < objects.length - 1; ++i) {
|
|
2707
|
-
const current = objects[i];
|
|
2708
|
-
const next = objects[i + 1];
|
|
2709
|
-
if (next.startTime - current.startTime <
|
|
2710
|
-
2000 * this.beatmap.general.stackLeniency &&
|
|
2711
|
-
next.position.getDistance(current.position) <
|
|
2712
|
-
Math.sqrt(convertedScale)) {
|
|
2713
|
-
next.stackHeight = current.stackHeight + 1;
|
|
2587
|
+
const convertedScale = CircleSizeCalculator.standardScaleToDroidScale(objects[0].scale);
|
|
2588
|
+
for (let i = 0; i < objects.length - 1; ++i) {
|
|
2589
|
+
const current = objects[i];
|
|
2590
|
+
const next = objects[i + 1];
|
|
2591
|
+
if (next.startTime - current.startTime <
|
|
2592
|
+
2000 * this.beatmap.general.stackLeniency &&
|
|
2593
|
+
next.position.getDistance(current.position) <
|
|
2594
|
+
Math.sqrt(convertedScale)) {
|
|
2595
|
+
next.stackHeight = current.stackHeight + 1;
|
|
2714
2596
|
}
|
|
2715
2597
|
}
|
|
2716
2598
|
}
|
|
@@ -2843,70 +2725,312 @@ class BeatmapProcessor {
|
|
|
2843
2725
|
}
|
|
2844
2726
|
}
|
|
2845
2727
|
}
|
|
2846
|
-
applyStandardOldStacking() {
|
|
2847
|
-
const objects = this.beatmap.hitObjects.objects;
|
|
2848
|
-
for (let i = 0; i < objects.length; ++i) {
|
|
2849
|
-
const currentObject = objects[i];
|
|
2850
|
-
if (currentObject.stackHeight !== 0 &&
|
|
2851
|
-
!(currentObject instanceof Slider)) {
|
|
2852
|
-
continue;
|
|
2728
|
+
applyStandardOldStacking() {
|
|
2729
|
+
const objects = this.beatmap.hitObjects.objects;
|
|
2730
|
+
for (let i = 0; i < objects.length; ++i) {
|
|
2731
|
+
const currentObject = objects[i];
|
|
2732
|
+
if (currentObject.stackHeight !== 0 &&
|
|
2733
|
+
!(currentObject instanceof Slider)) {
|
|
2734
|
+
continue;
|
|
2735
|
+
}
|
|
2736
|
+
let startTime = currentObject.endTime;
|
|
2737
|
+
let sliderStack = 0;
|
|
2738
|
+
const stackThreshold = currentObject.timePreempt * this.beatmap.general.stackLeniency;
|
|
2739
|
+
for (let j = i + 1; j < objects.length; ++j) {
|
|
2740
|
+
if (objects[j].startTime - stackThreshold > startTime) {
|
|
2741
|
+
break;
|
|
2742
|
+
}
|
|
2743
|
+
// Note the use of `startTime` in the code below doesn't match osu!stable's use of `endTime`.
|
|
2744
|
+
// This is because in osu!stable's implementation, `UpdateCalculations` is not called on the inner-loop hitobject (j)
|
|
2745
|
+
// and therefore it does not have a correct `endTime`, but instead the default of `endTime = startTime`.
|
|
2746
|
+
//
|
|
2747
|
+
// Effects of this can be seen on https://osu.ppy.sh/beatmapsets/243#osu/1146 at sliders around 86647 ms, where
|
|
2748
|
+
// if we use `endTime` here it would result in unexpected stacking.
|
|
2749
|
+
//
|
|
2750
|
+
// Reference: https://github.com/ppy/osu/pull/24188
|
|
2751
|
+
if (objects[j].position.getDistance(currentObject.position) <
|
|
2752
|
+
BeatmapProcessor.stackDistance) {
|
|
2753
|
+
++currentObject.stackHeight;
|
|
2754
|
+
startTime = objects[j].startTime;
|
|
2755
|
+
}
|
|
2756
|
+
else if (objects[j].position.getDistance(currentObject.endPosition) <
|
|
2757
|
+
BeatmapProcessor.stackDistance) {
|
|
2758
|
+
// Case for sliders - bump notes down and right, rather than up and left.
|
|
2759
|
+
++sliderStack;
|
|
2760
|
+
objects[j].stackHeight -= sliderStack;
|
|
2761
|
+
startTime = objects[j].startTime;
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
BeatmapProcessor.stackDistance = 3;
|
|
2768
|
+
|
|
2769
|
+
/**
|
|
2770
|
+
* Converts a beatmap for another mode.
|
|
2771
|
+
*/
|
|
2772
|
+
class BeatmapConverter {
|
|
2773
|
+
constructor(beatmap) {
|
|
2774
|
+
this.beatmap = beatmap;
|
|
2775
|
+
}
|
|
2776
|
+
/**
|
|
2777
|
+
* Converts the beatmap.
|
|
2778
|
+
*
|
|
2779
|
+
* @returns The converted beatmap.
|
|
2780
|
+
*/
|
|
2781
|
+
convert() {
|
|
2782
|
+
const converted = new Beatmap(this.beatmap);
|
|
2783
|
+
// Shallow clone isn't enough to ensure we don't mutate some beatmap properties unexpectedly.
|
|
2784
|
+
converted.difficulty = new BeatmapDifficulty(this.beatmap.difficulty);
|
|
2785
|
+
converted.hitObjects = this.convertHitObjects();
|
|
2786
|
+
return converted;
|
|
2787
|
+
}
|
|
2788
|
+
convertHitObjects() {
|
|
2789
|
+
const hitObjects = new BeatmapHitObjects();
|
|
2790
|
+
this.beatmap.hitObjects.objects.forEach((hitObject) => {
|
|
2791
|
+
hitObjects.add(this.convertHitObject(hitObject));
|
|
2792
|
+
});
|
|
2793
|
+
return hitObjects;
|
|
2794
|
+
}
|
|
2795
|
+
convertHitObject(hitObject) {
|
|
2796
|
+
let object;
|
|
2797
|
+
if (hitObject instanceof Circle) {
|
|
2798
|
+
object = new Circle({
|
|
2799
|
+
startTime: hitObject.startTime,
|
|
2800
|
+
position: hitObject.position,
|
|
2801
|
+
newCombo: hitObject.isNewCombo,
|
|
2802
|
+
type: hitObject.type,
|
|
2803
|
+
comboOffset: hitObject.comboOffset,
|
|
2804
|
+
});
|
|
2805
|
+
}
|
|
2806
|
+
else if (hitObject instanceof Slider) {
|
|
2807
|
+
object = new Slider({
|
|
2808
|
+
startTime: hitObject.startTime,
|
|
2809
|
+
position: hitObject.position,
|
|
2810
|
+
newCombo: hitObject.isNewCombo,
|
|
2811
|
+
type: hitObject.type,
|
|
2812
|
+
path: hitObject.path,
|
|
2813
|
+
repeatCount: hitObject.repeatCount,
|
|
2814
|
+
nodeSamples: hitObject.nodeSamples,
|
|
2815
|
+
comboOffset: hitObject.comboOffset,
|
|
2816
|
+
tickDistanceMultiplier:
|
|
2817
|
+
// Prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance.
|
|
2818
|
+
// This results in more (or less) ticks being generated in <v8 maps for the same time duration.
|
|
2819
|
+
this.beatmap.formatVersion < 8
|
|
2820
|
+
? 1 /
|
|
2821
|
+
this.beatmap.controlPoints.difficulty.controlPointAt(hitObject.startTime).speedMultiplier
|
|
2822
|
+
: 1,
|
|
2823
|
+
});
|
|
2824
|
+
}
|
|
2825
|
+
else {
|
|
2826
|
+
object = new Spinner({
|
|
2827
|
+
startTime: hitObject.startTime,
|
|
2828
|
+
endTime: hitObject.endTime,
|
|
2829
|
+
type: hitObject.type,
|
|
2830
|
+
});
|
|
2831
|
+
}
|
|
2832
|
+
object.samples = hitObject.samples;
|
|
2833
|
+
object.auxiliarySamples = hitObject.auxiliarySamples;
|
|
2834
|
+
return object;
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
|
|
2838
|
+
/**
|
|
2839
|
+
* Represents a beatmap with advanced information.
|
|
2840
|
+
*/
|
|
2841
|
+
class Beatmap {
|
|
2842
|
+
constructor(shallowCopy) {
|
|
2843
|
+
if (shallowCopy) {
|
|
2844
|
+
this.formatVersion = shallowCopy.formatVersion;
|
|
2845
|
+
this.general = shallowCopy.general;
|
|
2846
|
+
this.editor = shallowCopy.editor;
|
|
2847
|
+
this.metadata = shallowCopy.metadata;
|
|
2848
|
+
this.difficulty = shallowCopy.difficulty;
|
|
2849
|
+
this.events = shallowCopy.events;
|
|
2850
|
+
this.controlPoints = shallowCopy.controlPoints;
|
|
2851
|
+
this.colors = shallowCopy.colors;
|
|
2852
|
+
this.hitObjects = shallowCopy.hitObjects;
|
|
2853
|
+
return;
|
|
2854
|
+
}
|
|
2855
|
+
this.formatVersion = 1;
|
|
2856
|
+
this.general = new BeatmapGeneral();
|
|
2857
|
+
this.editor = new BeatmapEditor();
|
|
2858
|
+
this.metadata = new BeatmapMetadata();
|
|
2859
|
+
this.difficulty = new BeatmapDifficulty();
|
|
2860
|
+
this.events = new BeatmapEvents();
|
|
2861
|
+
this.controlPoints = new BeatmapControlPoints();
|
|
2862
|
+
this.colors = new BeatmapColor();
|
|
2863
|
+
this.hitObjects = new BeatmapHitObjects();
|
|
2864
|
+
}
|
|
2865
|
+
/**
|
|
2866
|
+
* The maximum combo of the beatmap.
|
|
2867
|
+
*/
|
|
2868
|
+
get maxCombo() {
|
|
2869
|
+
return (this.hitObjects.circles +
|
|
2870
|
+
this.hitObjects.sliders +
|
|
2871
|
+
this.hitObjects.sliderTicks +
|
|
2872
|
+
this.hitObjects.sliderRepeatPoints +
|
|
2873
|
+
this.hitObjects.sliderEnds +
|
|
2874
|
+
this.hitObjects.spinners);
|
|
2875
|
+
}
|
|
2876
|
+
/**
|
|
2877
|
+
* The most common beat length of the beatmap.
|
|
2878
|
+
*/
|
|
2879
|
+
get mostCommonBeatLength() {
|
|
2880
|
+
var _a, _b, _c, _d, _e;
|
|
2881
|
+
// The last playable time in the beatmap - the last timing point extends to this time.
|
|
2882
|
+
// 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.
|
|
2883
|
+
const lastTime = (_d = (_b = (_a = this.hitObjects.objects[this.hitObjects.objects.length - 1]) === null || _a === void 0 ? void 0 : _a.endTime) !== null && _b !== void 0 ? _b : (_c = this.controlPoints.timing.points[this.controlPoints.timing.points.length - 1]) === null || _c === void 0 ? void 0 : _c.time) !== null && _d !== void 0 ? _d : 0;
|
|
2884
|
+
const mostCommon =
|
|
2885
|
+
// Construct a set of {beatLength, duration} objects for each individual timing point.
|
|
2886
|
+
this.controlPoints.timing.points
|
|
2887
|
+
.map((t, i, a) => {
|
|
2888
|
+
if (t.time > lastTime) {
|
|
2889
|
+
return { beatLength: t.msPerBeat, duration: 0 };
|
|
2890
|
+
}
|
|
2891
|
+
// osu-stable forced the first control point to start at 0.
|
|
2892
|
+
const currentTime = i === 0 ? 0 : t.time;
|
|
2893
|
+
const nextTime = i === a.length - 1 ? lastTime : a[i + 1].time;
|
|
2894
|
+
return {
|
|
2895
|
+
beatLength: t.msPerBeat,
|
|
2896
|
+
duration: nextTime - currentTime,
|
|
2897
|
+
};
|
|
2898
|
+
})
|
|
2899
|
+
// Get the most common one, or 0 as a suitable default.
|
|
2900
|
+
.sort((a, b) => b.duration - a.duration)[0];
|
|
2901
|
+
return (_e = mostCommon === null || mostCommon === void 0 ? void 0 : mostCommon.beatLength) !== null && _e !== void 0 ? _e : 0;
|
|
2902
|
+
}
|
|
2903
|
+
/**
|
|
2904
|
+
* Returns a time combined with beatmap-wide time offset.
|
|
2905
|
+
*
|
|
2906
|
+
* BeatmapVersion 4 and lower had an incorrect offset. Stable has this set as 24ms off.
|
|
2907
|
+
*
|
|
2908
|
+
* @param time The time.
|
|
2909
|
+
*/
|
|
2910
|
+
getOffsetTime(time) {
|
|
2911
|
+
return time + (this.formatVersion < 5 ? 24 : 0);
|
|
2912
|
+
}
|
|
2913
|
+
/**
|
|
2914
|
+
* Calculates the osu!droid maximum score of the beatmap without taking spinner bonus into account.
|
|
2915
|
+
*
|
|
2916
|
+
* @param mods The modifications to calculate for. Defaults to No Mod.
|
|
2917
|
+
* @param customSpeedMultiplier The custom speed multiplier of the beatmap. Defaults to 1.
|
|
2918
|
+
*/
|
|
2919
|
+
maxDroidScore(mods = [], customSpeedMultiplier = 1) {
|
|
2920
|
+
let scoreMultiplier = 1;
|
|
2921
|
+
for (const mod of mods) {
|
|
2922
|
+
if (mod.isApplicableToDroid()) {
|
|
2923
|
+
scoreMultiplier *= mod.droidScoreMultiplier;
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
if (customSpeedMultiplier >= 1) {
|
|
2927
|
+
scoreMultiplier *= 1 + (customSpeedMultiplier - 1) * 0.24;
|
|
2928
|
+
}
|
|
2929
|
+
else {
|
|
2930
|
+
scoreMultiplier *= Math.pow(0.3, (1 - customSpeedMultiplier) * 4);
|
|
2931
|
+
}
|
|
2932
|
+
const difficultyMultiplier = 1 +
|
|
2933
|
+
this.difficulty.od / 10 +
|
|
2934
|
+
this.difficulty.hp / 10 +
|
|
2935
|
+
(this.difficulty.cs - 3) / 4;
|
|
2936
|
+
let combo = 0;
|
|
2937
|
+
let score = 0;
|
|
2938
|
+
for (const object of this.hitObjects.objects) {
|
|
2939
|
+
if (!(object instanceof Slider)) {
|
|
2940
|
+
score += Math.floor(300 + (300 * combo * difficultyMultiplier) / 25);
|
|
2941
|
+
++combo;
|
|
2942
|
+
continue;
|
|
2943
|
+
}
|
|
2944
|
+
const { ticks } = object;
|
|
2945
|
+
// Apply slider head.
|
|
2946
|
+
score += 30;
|
|
2947
|
+
++combo;
|
|
2948
|
+
// Apply slider repeats.
|
|
2949
|
+
score += 30 * object.repeatCount;
|
|
2950
|
+
combo += object.repeatCount;
|
|
2951
|
+
// Apply slider ticks.
|
|
2952
|
+
score += 10 * ticks;
|
|
2953
|
+
combo += ticks;
|
|
2954
|
+
// Apply slider end.
|
|
2955
|
+
score += Math.floor(300 + (300 * combo * difficultyMultiplier) / 25);
|
|
2956
|
+
++combo;
|
|
2957
|
+
}
|
|
2958
|
+
return Math.floor(score * scoreMultiplier);
|
|
2959
|
+
}
|
|
2960
|
+
/**
|
|
2961
|
+
* Calculates the osu!standard maximum score of the beatmap without taking spinner bonus into account.
|
|
2962
|
+
*
|
|
2963
|
+
* @param mods The modifications to calculate for. Defaults to No Mod.
|
|
2964
|
+
*/
|
|
2965
|
+
maxOsuScore(mods = []) {
|
|
2966
|
+
const accumulatedDiffPoints = this.difficulty.cs + this.difficulty.hp + this.difficulty.od;
|
|
2967
|
+
let difficultyMultiplier = 2;
|
|
2968
|
+
let scoreMultiplier = 1;
|
|
2969
|
+
for (const mod of mods) {
|
|
2970
|
+
if (mod.isApplicableToOsu()) {
|
|
2971
|
+
scoreMultiplier *= mod.pcScoreMultiplier;
|
|
2853
2972
|
}
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2973
|
+
}
|
|
2974
|
+
switch (true) {
|
|
2975
|
+
case accumulatedDiffPoints <= 5:
|
|
2976
|
+
difficultyMultiplier = 2;
|
|
2977
|
+
break;
|
|
2978
|
+
case accumulatedDiffPoints <= 12:
|
|
2979
|
+
difficultyMultiplier = 3;
|
|
2980
|
+
break;
|
|
2981
|
+
case accumulatedDiffPoints <= 17:
|
|
2982
|
+
difficultyMultiplier = 4;
|
|
2983
|
+
break;
|
|
2984
|
+
case accumulatedDiffPoints <= 24:
|
|
2985
|
+
difficultyMultiplier = 5;
|
|
2986
|
+
break;
|
|
2987
|
+
case accumulatedDiffPoints >= 25:
|
|
2988
|
+
difficultyMultiplier = 6;
|
|
2989
|
+
break;
|
|
2990
|
+
}
|
|
2991
|
+
let combo = 0;
|
|
2992
|
+
let score = 0;
|
|
2993
|
+
for (const object of this.hitObjects.objects) {
|
|
2994
|
+
if (!(object instanceof Slider)) {
|
|
2995
|
+
score += Math.floor(300 +
|
|
2996
|
+
(300 * combo * difficultyMultiplier * scoreMultiplier) /
|
|
2997
|
+
25);
|
|
2998
|
+
++combo;
|
|
2999
|
+
continue;
|
|
2881
3000
|
}
|
|
3001
|
+
const { ticks } = object;
|
|
3002
|
+
// Apply slider head.
|
|
3003
|
+
score += 30;
|
|
3004
|
+
++combo;
|
|
3005
|
+
// Apply slider repeats.
|
|
3006
|
+
score += 30 * object.repeatCount;
|
|
3007
|
+
combo += object.repeatCount;
|
|
3008
|
+
// Apply slider ticks.
|
|
3009
|
+
score += 10 * ticks;
|
|
3010
|
+
combo += ticks;
|
|
3011
|
+
// Apply slider end.
|
|
3012
|
+
score += Math.floor(300 +
|
|
3013
|
+
(300 * combo * difficultyMultiplier * scoreMultiplier) / 25);
|
|
3014
|
+
++combo;
|
|
2882
3015
|
}
|
|
2883
|
-
|
|
2884
|
-
}
|
|
2885
|
-
BeatmapProcessor.stackDistance = 3;
|
|
2886
|
-
|
|
2887
|
-
/**
|
|
2888
|
-
* Converts a beatmap for another mode.
|
|
2889
|
-
*/
|
|
2890
|
-
class BeatmapConverter {
|
|
2891
|
-
constructor(beatmap) {
|
|
2892
|
-
this.beatmap = beatmap;
|
|
3016
|
+
return score;
|
|
2893
3017
|
}
|
|
2894
3018
|
/**
|
|
2895
|
-
*
|
|
3019
|
+
* Constructs a playable `Beatmap` from this `Beatmap`.
|
|
2896
3020
|
*
|
|
2897
|
-
*
|
|
2898
|
-
*
|
|
3021
|
+
* The returned `Beatmap` is in a playable state - all `HitObject` and `BeatmapDifficulty` `Mod`s
|
|
3022
|
+
* have been applied, and `HitObject`s have been fully constructed.
|
|
3023
|
+
*
|
|
3024
|
+
* @param options The options to use.
|
|
3025
|
+
* @return The constructed `Beatmap`.
|
|
2899
3026
|
*/
|
|
2900
|
-
|
|
3027
|
+
createPlayableBeatmap(options) {
|
|
2901
3028
|
var _a, _b, _c;
|
|
2902
3029
|
const mods = (_a = options === null || options === void 0 ? void 0 : options.mods) !== null && _a !== void 0 ? _a : [];
|
|
2903
3030
|
const mode = (_b = options === null || options === void 0 ? void 0 : options.mode) !== null && _b !== void 0 ? _b : exports.Modes.osu;
|
|
2904
3031
|
const customSpeedMultiplier = (_c = options === null || options === void 0 ? void 0 : options.customSpeedMultiplier) !== null && _c !== void 0 ? _c : 1;
|
|
2905
3032
|
// Convert
|
|
2906
|
-
const converted = new
|
|
2907
|
-
// Shallow clone isn't enough to ensure we don't mutate some beatmap properties unexpectedly.
|
|
2908
|
-
converted.difficulty = new BeatmapDifficulty(this.beatmap.difficulty);
|
|
2909
|
-
converted.hitObjects = this.convertHitObjects();
|
|
3033
|
+
const converted = new BeatmapConverter(this).convert();
|
|
2910
3034
|
// Apply difficulty mods
|
|
2911
3035
|
mods.forEach((mod) => {
|
|
2912
3036
|
if (mod.isApplicableToDifficulty()) {
|
|
@@ -2921,7 +3045,6 @@ class BeatmapConverter {
|
|
|
2921
3045
|
mod.applyToDifficultyWithSettings(mode, converted.difficulty, mods, customSpeedMultiplier);
|
|
2922
3046
|
}
|
|
2923
3047
|
});
|
|
2924
|
-
const processor = new BeatmapProcessor(converted);
|
|
2925
3048
|
// Compute default values for hit objects, including creating nested hit objects in-case they're needed.
|
|
2926
3049
|
converted.hitObjects.objects.forEach((hitObject) => hitObject.applyDefaults(converted.controlPoints, converted.difficulty, mode));
|
|
2927
3050
|
mods.forEach((mod) => {
|
|
@@ -2931,7 +3054,7 @@ class BeatmapConverter {
|
|
|
2931
3054
|
}
|
|
2932
3055
|
}
|
|
2933
3056
|
});
|
|
2934
|
-
|
|
3057
|
+
new BeatmapProcessor(converted).postProcess(mode);
|
|
2935
3058
|
mods.forEach((mod) => {
|
|
2936
3059
|
if (mod.isApplicableToBeatmap()) {
|
|
2937
3060
|
mod.applyToBeatmap(converted);
|
|
@@ -2939,52 +3062,57 @@ class BeatmapConverter {
|
|
|
2939
3062
|
});
|
|
2940
3063
|
return converted;
|
|
2941
3064
|
}
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
position: hitObject.position,
|
|
2955
|
-
newCombo: hitObject.isNewCombo,
|
|
2956
|
-
type: hitObject.type,
|
|
2957
|
-
comboOffset: hitObject.comboOffset,
|
|
2958
|
-
});
|
|
2959
|
-
}
|
|
2960
|
-
else if (hitObject instanceof Slider) {
|
|
2961
|
-
object = new Slider({
|
|
2962
|
-
startTime: hitObject.startTime,
|
|
2963
|
-
position: hitObject.position,
|
|
2964
|
-
newCombo: hitObject.isNewCombo,
|
|
2965
|
-
type: hitObject.type,
|
|
2966
|
-
path: hitObject.path,
|
|
2967
|
-
repeatCount: hitObject.repeatCount,
|
|
2968
|
-
nodeSamples: hitObject.nodeSamples,
|
|
2969
|
-
comboOffset: hitObject.comboOffset,
|
|
2970
|
-
tickDistanceMultiplier:
|
|
2971
|
-
// Prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance.
|
|
2972
|
-
// This results in more (or less) ticks being generated in <v8 maps for the same time duration.
|
|
2973
|
-
this.beatmap.formatVersion < 8
|
|
2974
|
-
? 1 /
|
|
2975
|
-
this.beatmap.controlPoints.difficulty.controlPointAt(hitObject.startTime).speedMultiplier
|
|
2976
|
-
: 1,
|
|
2977
|
-
});
|
|
2978
|
-
}
|
|
2979
|
-
else {
|
|
2980
|
-
object = new Spinner({
|
|
2981
|
-
startTime: hitObject.startTime,
|
|
2982
|
-
endTime: hitObject.endTime,
|
|
2983
|
-
type: hitObject.type,
|
|
2984
|
-
});
|
|
3065
|
+
/**
|
|
3066
|
+
* Returns a string representative of the class.
|
|
3067
|
+
*/
|
|
3068
|
+
toString() {
|
|
3069
|
+
let res = this.metadata.artist + " - " + this.metadata.title + " [";
|
|
3070
|
+
if (this.metadata.titleUnicode || this.metadata.artistUnicode) {
|
|
3071
|
+
res +=
|
|
3072
|
+
"(" +
|
|
3073
|
+
this.metadata.artistUnicode +
|
|
3074
|
+
" - " +
|
|
3075
|
+
this.metadata.titleUnicode +
|
|
3076
|
+
")";
|
|
2985
3077
|
}
|
|
2986
|
-
|
|
2987
|
-
|
|
3078
|
+
res +=
|
|
3079
|
+
this.metadata.version +
|
|
3080
|
+
"] mapped by " +
|
|
3081
|
+
this.metadata.creator +
|
|
3082
|
+
"\n" +
|
|
3083
|
+
"\n" +
|
|
3084
|
+
"AR" +
|
|
3085
|
+
MathUtils.round(this.difficulty.ar, 2) +
|
|
3086
|
+
" " +
|
|
3087
|
+
"OD" +
|
|
3088
|
+
MathUtils.round(this.difficulty.od, 2) +
|
|
3089
|
+
" " +
|
|
3090
|
+
"CS" +
|
|
3091
|
+
MathUtils.round(this.difficulty.cs, 2) +
|
|
3092
|
+
" " +
|
|
3093
|
+
"HP" +
|
|
3094
|
+
MathUtils.round(this.difficulty.hp, 2) +
|
|
3095
|
+
"\n" +
|
|
3096
|
+
this.hitObjects.circles +
|
|
3097
|
+
" circles, " +
|
|
3098
|
+
this.hitObjects.sliders +
|
|
3099
|
+
" sliders, " +
|
|
3100
|
+
this.hitObjects.spinners +
|
|
3101
|
+
" spinners" +
|
|
3102
|
+
"\n" +
|
|
3103
|
+
this.maxCombo +
|
|
3104
|
+
" max combo";
|
|
3105
|
+
return res;
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
|
|
3109
|
+
/**
|
|
3110
|
+
* Represents a beatmap's background.
|
|
3111
|
+
*/
|
|
3112
|
+
class BeatmapBackground {
|
|
3113
|
+
constructor(filename, offset) {
|
|
3114
|
+
this.filename = filename;
|
|
3115
|
+
this.offset = offset;
|
|
2988
3116
|
}
|
|
2989
3117
|
}
|
|
2990
3118
|
|
|
@@ -7404,6 +7532,20 @@ class Interpolation {
|
|
|
7404
7532
|
}
|
|
7405
7533
|
}
|
|
7406
7534
|
|
|
7535
|
+
/**
|
|
7536
|
+
* Ranking status of a beatmap.
|
|
7537
|
+
*/
|
|
7538
|
+
exports.RankedStatus = void 0;
|
|
7539
|
+
(function (RankedStatus) {
|
|
7540
|
+
RankedStatus[RankedStatus["graveyard"] = -2] = "graveyard";
|
|
7541
|
+
RankedStatus[RankedStatus["wip"] = -1] = "wip";
|
|
7542
|
+
RankedStatus[RankedStatus["pending"] = 0] = "pending";
|
|
7543
|
+
RankedStatus[RankedStatus["ranked"] = 1] = "ranked";
|
|
7544
|
+
RankedStatus[RankedStatus["approved"] = 2] = "approved";
|
|
7545
|
+
RankedStatus[RankedStatus["qualified"] = 3] = "qualified";
|
|
7546
|
+
RankedStatus[RankedStatus["loved"] = 4] = "loved";
|
|
7547
|
+
})(exports.RankedStatus || (exports.RankedStatus = {}));
|
|
7548
|
+
|
|
7407
7549
|
/**
|
|
7408
7550
|
* An API request builder for osu!standard.
|
|
7409
7551
|
*/
|
|
@@ -7448,7 +7590,7 @@ class MapInfo {
|
|
|
7448
7590
|
/**
|
|
7449
7591
|
* The ranking status of the beatmap.
|
|
7450
7592
|
*/
|
|
7451
|
-
this.approved =
|
|
7593
|
+
this.approved = exports.RankedStatus.pending;
|
|
7452
7594
|
/**
|
|
7453
7595
|
* The ID of the beatmap.
|
|
7454
7596
|
*/
|
|
@@ -7520,7 +7662,7 @@ class MapInfo {
|
|
|
7520
7662
|
/**
|
|
7521
7663
|
* The maximum combo of the beatmap.
|
|
7522
7664
|
*/
|
|
7523
|
-
this.maxCombo =
|
|
7665
|
+
this.maxCombo = null;
|
|
7524
7666
|
/**
|
|
7525
7667
|
* The circle size of the beatmap.
|
|
7526
7668
|
*/
|
|
@@ -7676,7 +7818,8 @@ class MapInfo {
|
|
|
7676
7818
|
map.spinners = mapinfo.count_spinner
|
|
7677
7819
|
? parseInt(mapinfo.count_spinner)
|
|
7678
7820
|
: 0;
|
|
7679
|
-
map.maxCombo =
|
|
7821
|
+
map.maxCombo =
|
|
7822
|
+
mapinfo.max_combo !== null ? parseInt(mapinfo.max_combo) : null;
|
|
7680
7823
|
map.cs = parseFloat(mapinfo.diff_size);
|
|
7681
7824
|
map.ar = parseFloat(mapinfo.diff_approach);
|
|
7682
7825
|
map.od = parseFloat(mapinfo.diff_overall);
|
|
@@ -7707,7 +7850,7 @@ class MapInfo {
|
|
|
7707
7850
|
* @returns The raw API response represented by this `MapInfo`.
|
|
7708
7851
|
*/
|
|
7709
7852
|
toAPIResponse() {
|
|
7710
|
-
var _a, _b, _c, _d, _e, _f;
|
|
7853
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
7711
7854
|
const padDateNumber = (num) => num.toString().padStart(2, "0");
|
|
7712
7855
|
const convertDate = (date) => `${date.getUTCFullYear()}-${padDateNumber(date.getUTCMonth() + 1)}-${padDateNumber(date.getUTCDate())} ${padDateNumber(date.getUTCHours())}:${padDateNumber(date.getUTCMinutes())}:${padDateNumber(date.getUTCSeconds())}`;
|
|
7713
7856
|
return {
|
|
@@ -7748,7 +7891,7 @@ class MapInfo {
|
|
|
7748
7891
|
count_normal: this.circles.toString(),
|
|
7749
7892
|
count_slider: this.sliders.toString(),
|
|
7750
7893
|
count_spinner: this.spinners.toString(),
|
|
7751
|
-
max_combo: this.maxCombo.toString(),
|
|
7894
|
+
max_combo: (_h = (_g = this.maxCombo) === null || _g === void 0 ? void 0 : _g.toString()) !== null && _h !== void 0 ? _h : null,
|
|
7752
7895
|
storyboard: this.storyboardAvailable ? "1" : "0",
|
|
7753
7896
|
video: this.videoAvailable ? "1" : "0",
|
|
7754
7897
|
download_unavailable: this.downloadAvailable ? "0" : "1",
|
|
@@ -7882,6 +8025,16 @@ class ModFlashlight extends Mod {
|
|
|
7882
8025
|
}
|
|
7883
8026
|
}
|
|
7884
8027
|
|
|
8028
|
+
/**
|
|
8029
|
+
* Represents the osu! playfield.
|
|
8030
|
+
*/
|
|
8031
|
+
class Playfield {
|
|
8032
|
+
}
|
|
8033
|
+
/**
|
|
8034
|
+
* The size of the playfield, which is 512x384.
|
|
8035
|
+
*/
|
|
8036
|
+
Playfield.baseSize = new Vector2(512, 384);
|
|
8037
|
+
|
|
7885
8038
|
/**
|
|
7886
8039
|
* Represents the HardRock mod.
|
|
7887
8040
|
*/
|
|
@@ -7914,6 +8067,29 @@ class ModHardRock extends Mod {
|
|
|
7914
8067
|
difficulty.od = this.applySetting(difficulty.od);
|
|
7915
8068
|
difficulty.hp = this.applySetting(difficulty.hp);
|
|
7916
8069
|
}
|
|
8070
|
+
applyToHitObject(_, hitObject) {
|
|
8071
|
+
// Reflect the position of the hit object.
|
|
8072
|
+
hitObject.position = this.reflectVector(hitObject.position);
|
|
8073
|
+
if (!(hitObject instanceof Slider)) {
|
|
8074
|
+
return;
|
|
8075
|
+
}
|
|
8076
|
+
// Reflect the control points of the slider. This will reflect the positions of head and tail circles.
|
|
8077
|
+
hitObject.path = new SliderPath({
|
|
8078
|
+
pathType: hitObject.path.pathType,
|
|
8079
|
+
controlPoints: hitObject.path.controlPoints.map((v) => this.reflectControlPoint(v)),
|
|
8080
|
+
expectedDistance: hitObject.path.expectedDistance,
|
|
8081
|
+
});
|
|
8082
|
+
// Reflect the position of slider ticks and repeats.
|
|
8083
|
+
hitObject.nestedHitObjects.forEach((obj) => {
|
|
8084
|
+
obj.position = this.reflectVector(obj.position);
|
|
8085
|
+
});
|
|
8086
|
+
}
|
|
8087
|
+
reflectVector(vector) {
|
|
8088
|
+
return new Vector2(vector.x, Playfield.baseSize.y - vector.y);
|
|
8089
|
+
}
|
|
8090
|
+
reflectControlPoint(vector) {
|
|
8091
|
+
return new Vector2(vector.x, -vector.y);
|
|
8092
|
+
}
|
|
7917
8093
|
applySetting(value, ratio = 1.4) {
|
|
7918
8094
|
return Math.min(value * ratio, 10);
|
|
7919
8095
|
}
|
|
@@ -8341,30 +8517,6 @@ class NormalDistribution {
|
|
|
8341
8517
|
}
|
|
8342
8518
|
}
|
|
8343
8519
|
|
|
8344
|
-
/**
|
|
8345
|
-
* Represents the osu! playfield.
|
|
8346
|
-
*/
|
|
8347
|
-
class Playfield {
|
|
8348
|
-
}
|
|
8349
|
-
/**
|
|
8350
|
-
* The size of the playfield, which is 512x384.
|
|
8351
|
-
*/
|
|
8352
|
-
Playfield.baseSize = new Vector2(512, 384);
|
|
8353
|
-
|
|
8354
|
-
/**
|
|
8355
|
-
* Ranking status of a beatmap.
|
|
8356
|
-
*/
|
|
8357
|
-
exports.RankedStatus = void 0;
|
|
8358
|
-
(function (RankedStatus) {
|
|
8359
|
-
RankedStatus[RankedStatus["graveyard"] = -2] = "graveyard";
|
|
8360
|
-
RankedStatus[RankedStatus["wip"] = -1] = "wip";
|
|
8361
|
-
RankedStatus[RankedStatus["pending"] = 0] = "pending";
|
|
8362
|
-
RankedStatus[RankedStatus["ranked"] = 1] = "ranked";
|
|
8363
|
-
RankedStatus[RankedStatus["approved"] = 2] = "approved";
|
|
8364
|
-
RankedStatus[RankedStatus["qualified"] = 3] = "qualified";
|
|
8365
|
-
RankedStatus[RankedStatus["loved"] = 4] = "loved";
|
|
8366
|
-
})(exports.RankedStatus || (exports.RankedStatus = {}));
|
|
8367
|
-
|
|
8368
8520
|
dotenv.config();
|
|
8369
8521
|
|
|
8370
8522
|
exports.AR0_MS = AR0_MS;
|