@rian8337/osu-base 4.0.0-beta.68 → 4.0.0-beta.70
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 +958 -562
- package/package.json +2 -2
- package/typings/index.d.ts +198 -44
package/dist/index.js
CHANGED
|
@@ -1040,6 +1040,35 @@ class HitObject {
|
|
|
1040
1040
|
set scale(value) {
|
|
1041
1041
|
this._scale = value;
|
|
1042
1042
|
}
|
|
1043
|
+
/**
|
|
1044
|
+
* The multiplier for the stack offset of this hitobject.
|
|
1045
|
+
*
|
|
1046
|
+
* This determines how much hitobjects are stacked - and to which direction.
|
|
1047
|
+
*/
|
|
1048
|
+
get stackOffsetMultiplier() {
|
|
1049
|
+
return this._stackOffsetMultiplier;
|
|
1050
|
+
}
|
|
1051
|
+
set stackOffsetMultiplier(value) {
|
|
1052
|
+
this._stackOffsetMultiplier = value;
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* The stack offset vector of this hitobject.
|
|
1056
|
+
*/
|
|
1057
|
+
get stackOffset() {
|
|
1058
|
+
return new Vector2(this.stackHeight * this.scale * this.stackOffsetMultiplier);
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* The stacked position of this hitobject.
|
|
1062
|
+
*/
|
|
1063
|
+
get stackedPosition() {
|
|
1064
|
+
return this.evaluateStackedPosition(this.position);
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* The stacked end position of this hitobject.
|
|
1068
|
+
*/
|
|
1069
|
+
get stackedEndPosition() {
|
|
1070
|
+
return this.evaluateStackedPosition(this.endPosition);
|
|
1071
|
+
}
|
|
1043
1072
|
/**
|
|
1044
1073
|
* The hitobject type (circle, slider, or spinner).
|
|
1045
1074
|
*/
|
|
@@ -1089,6 +1118,7 @@ class HitObject {
|
|
|
1089
1118
|
this.hitWindow = null;
|
|
1090
1119
|
this._stackHeight = 0;
|
|
1091
1120
|
this._scale = 1;
|
|
1121
|
+
this._stackOffsetMultiplier = 0;
|
|
1092
1122
|
/**
|
|
1093
1123
|
* The time at which the approach circle of this hitobject should appear before this hitobject starts.
|
|
1094
1124
|
*/
|
|
@@ -1127,9 +1157,11 @@ class HitObject {
|
|
|
1127
1157
|
switch (mode) {
|
|
1128
1158
|
case exports.Modes.droid:
|
|
1129
1159
|
this.scale = CircleSizeCalculator.droidCSToDroidScale(difficulty.cs);
|
|
1160
|
+
this.stackOffsetMultiplier = -4;
|
|
1130
1161
|
break;
|
|
1131
1162
|
case exports.Modes.osu:
|
|
1132
1163
|
this.scale = CircleSizeCalculator.standardCSToStandardScale(difficulty.cs, true);
|
|
1164
|
+
this.stackOffsetMultiplier = -6.4;
|
|
1133
1165
|
break;
|
|
1134
1166
|
}
|
|
1135
1167
|
}
|
|
@@ -1164,35 +1196,6 @@ class HitObject {
|
|
|
1164
1196
|
}
|
|
1165
1197
|
}
|
|
1166
1198
|
}
|
|
1167
|
-
/**
|
|
1168
|
-
* Evaluates the stack offset vector of the hitobject.
|
|
1169
|
-
*
|
|
1170
|
-
* This is used to calculate offset for stacked positions.
|
|
1171
|
-
*
|
|
1172
|
-
* @param mode The gamemode to evaluate for.
|
|
1173
|
-
* @returns The stack offset with respect to the gamemode.
|
|
1174
|
-
*/
|
|
1175
|
-
getStackOffset(mode) {
|
|
1176
|
-
return new Vector2(this.stackHeight * this.scale * (mode === exports.Modes.droid ? -4 : -6.4));
|
|
1177
|
-
}
|
|
1178
|
-
/**
|
|
1179
|
-
* Evaluates the stacked position of the hitobject.
|
|
1180
|
-
*
|
|
1181
|
-
* @param mode The gamemode to evaluate for.
|
|
1182
|
-
* @returns The stacked position with respect to the gamemode.
|
|
1183
|
-
*/
|
|
1184
|
-
getStackedPosition(mode) {
|
|
1185
|
-
return this.evaluateStackedPosition(this.position, mode);
|
|
1186
|
-
}
|
|
1187
|
-
/**
|
|
1188
|
-
* Evaluates the stacked end position of the hitobject.
|
|
1189
|
-
*
|
|
1190
|
-
* @param mode The gamemode to evaluate for.
|
|
1191
|
-
* @returns The stacked end position with respect to the gamemode.
|
|
1192
|
-
*/
|
|
1193
|
-
getStackedEndPosition(mode) {
|
|
1194
|
-
return this.evaluateStackedPosition(this.endPosition, mode);
|
|
1195
|
-
}
|
|
1196
1199
|
/**
|
|
1197
1200
|
* Creates a hit sample info based on the sample setting of the first `BankHitSampleInfo.HIT_NORMAL` sample in the `samples` array.
|
|
1198
1201
|
* If no sample is available, sane default settings will be used instead.
|
|
@@ -1232,14 +1235,13 @@ class HitObject {
|
|
|
1232
1235
|
* Evaluates the stacked position of the specified position.
|
|
1233
1236
|
*
|
|
1234
1237
|
* @param position The position to evaluate.
|
|
1235
|
-
* @param mode The gamemode to evaluate for.
|
|
1236
1238
|
* @returns The stacked position.
|
|
1237
1239
|
*/
|
|
1238
|
-
evaluateStackedPosition(position
|
|
1239
|
-
if (this.type & exports.ObjectTypes.spinner) {
|
|
1240
|
+
evaluateStackedPosition(position) {
|
|
1241
|
+
if ((this.type & exports.ObjectTypes.spinner) > 0 || this.stackHeight === 0) {
|
|
1240
1242
|
return position;
|
|
1241
1243
|
}
|
|
1242
|
-
return position.add(this.
|
|
1244
|
+
return position.add(this.stackOffset);
|
|
1243
1245
|
}
|
|
1244
1246
|
}
|
|
1245
1247
|
/**
|
|
@@ -1263,10 +1265,64 @@ HitObject.preemptMin = 450;
|
|
|
1263
1265
|
*/
|
|
1264
1266
|
HitObject.controlPointLeniency = 1;
|
|
1265
1267
|
|
|
1268
|
+
/**
|
|
1269
|
+
* Types of easing.
|
|
1270
|
+
*
|
|
1271
|
+
* See {@link http://easings.net/ this} page for more samples.
|
|
1272
|
+
*/
|
|
1273
|
+
exports.Easing = void 0;
|
|
1274
|
+
(function (Easing) {
|
|
1275
|
+
Easing[Easing["none"] = 0] = "none";
|
|
1276
|
+
Easing[Easing["out"] = 1] = "out";
|
|
1277
|
+
Easing[Easing["in"] = 2] = "in";
|
|
1278
|
+
Easing[Easing["inQuad"] = 3] = "inQuad";
|
|
1279
|
+
Easing[Easing["outQuad"] = 4] = "outQuad";
|
|
1280
|
+
Easing[Easing["inOutQuad"] = 5] = "inOutQuad";
|
|
1281
|
+
Easing[Easing["inCubic"] = 6] = "inCubic";
|
|
1282
|
+
Easing[Easing["outCubic"] = 7] = "outCubic";
|
|
1283
|
+
Easing[Easing["inOutCubic"] = 8] = "inOutCubic";
|
|
1284
|
+
Easing[Easing["inQuart"] = 9] = "inQuart";
|
|
1285
|
+
Easing[Easing["outQuart"] = 10] = "outQuart";
|
|
1286
|
+
Easing[Easing["inOutQuart"] = 11] = "inOutQuart";
|
|
1287
|
+
Easing[Easing["inQuint"] = 12] = "inQuint";
|
|
1288
|
+
Easing[Easing["outQuint"] = 13] = "outQuint";
|
|
1289
|
+
Easing[Easing["inOutQuint"] = 14] = "inOutQuint";
|
|
1290
|
+
Easing[Easing["inSine"] = 15] = "inSine";
|
|
1291
|
+
Easing[Easing["outSine"] = 16] = "outSine";
|
|
1292
|
+
Easing[Easing["inOutSine"] = 17] = "inOutSine";
|
|
1293
|
+
Easing[Easing["inExpo"] = 18] = "inExpo";
|
|
1294
|
+
Easing[Easing["outExpo"] = 19] = "outExpo";
|
|
1295
|
+
Easing[Easing["inOutExpo"] = 20] = "inOutExpo";
|
|
1296
|
+
Easing[Easing["inCirc"] = 21] = "inCirc";
|
|
1297
|
+
Easing[Easing["outCirc"] = 22] = "outCirc";
|
|
1298
|
+
Easing[Easing["inOutCirc"] = 23] = "inOutCirc";
|
|
1299
|
+
Easing[Easing["inElastic"] = 24] = "inElastic";
|
|
1300
|
+
Easing[Easing["outElastic"] = 25] = "outElastic";
|
|
1301
|
+
Easing[Easing["outElasticHalf"] = 26] = "outElasticHalf";
|
|
1302
|
+
Easing[Easing["outElasticQuarter"] = 27] = "outElasticQuarter";
|
|
1303
|
+
Easing[Easing["inOutElastic"] = 28] = "inOutElastic";
|
|
1304
|
+
Easing[Easing["inBack"] = 29] = "inBack";
|
|
1305
|
+
Easing[Easing["outBack"] = 30] = "outBack";
|
|
1306
|
+
Easing[Easing["inOutBack"] = 31] = "inOutBack";
|
|
1307
|
+
Easing[Easing["inBounce"] = 32] = "inBounce";
|
|
1308
|
+
Easing[Easing["outBounce"] = 33] = "outBounce";
|
|
1309
|
+
Easing[Easing["inOutBounce"] = 34] = "inOutBounce";
|
|
1310
|
+
Easing[Easing["outPow10"] = 35] = "outPow10";
|
|
1311
|
+
})(exports.Easing || (exports.Easing = {}));
|
|
1312
|
+
|
|
1266
1313
|
/**
|
|
1267
1314
|
* Represents a `Mod` specific setting.
|
|
1268
1315
|
*/
|
|
1269
1316
|
class ModSetting {
|
|
1317
|
+
/**
|
|
1318
|
+
* The default value of this `ModSetting`.
|
|
1319
|
+
*/
|
|
1320
|
+
get defaultValue() {
|
|
1321
|
+
return this._value;
|
|
1322
|
+
}
|
|
1323
|
+
set defaultValue(value) {
|
|
1324
|
+
this._defaultValue = value;
|
|
1325
|
+
}
|
|
1270
1326
|
/**
|
|
1271
1327
|
* The value of this `ModSetting`.
|
|
1272
1328
|
*/
|
|
@@ -1296,7 +1352,7 @@ class ModSetting {
|
|
|
1296
1352
|
this.valueChangedListeners = new Set();
|
|
1297
1353
|
this.name = name;
|
|
1298
1354
|
this.description = description;
|
|
1299
|
-
this.
|
|
1355
|
+
this._defaultValue = defaultValue;
|
|
1300
1356
|
this._value = defaultValue;
|
|
1301
1357
|
}
|
|
1302
1358
|
/**
|
|
@@ -1344,19 +1400,29 @@ class Mod {
|
|
|
1344
1400
|
* `Mod`s that are incompatible with this `Mod`.
|
|
1345
1401
|
*/
|
|
1346
1402
|
this.incompatibleMods = new Set();
|
|
1403
|
+
this.settingsBacking = null;
|
|
1347
1404
|
}
|
|
1348
1405
|
/**
|
|
1349
1406
|
* `ModSetting`s that are specific to this `Mod`.
|
|
1350
1407
|
*/
|
|
1351
1408
|
get settings() {
|
|
1352
|
-
|
|
1409
|
+
if (this.settingsBacking !== null) {
|
|
1410
|
+
return this.settingsBacking;
|
|
1411
|
+
}
|
|
1412
|
+
this.settingsBacking = [];
|
|
1353
1413
|
for (const prop in this) {
|
|
1354
1414
|
const value = this[prop];
|
|
1355
1415
|
if (value instanceof ModSetting) {
|
|
1356
|
-
|
|
1416
|
+
this.settingsBacking.push(value);
|
|
1357
1417
|
}
|
|
1358
1418
|
}
|
|
1359
|
-
return
|
|
1419
|
+
return this.settingsBacking;
|
|
1420
|
+
}
|
|
1421
|
+
/**
|
|
1422
|
+
* Whether all `ModSetting`s of this `Mod` are set to their default values.
|
|
1423
|
+
*/
|
|
1424
|
+
get usesDefaultSettings() {
|
|
1425
|
+
return this.settings.every((s) => s.isDefault);
|
|
1360
1426
|
}
|
|
1361
1427
|
/**
|
|
1362
1428
|
* Serializes this `Mod` to a `SerializedMod`.
|
|
@@ -1464,7 +1530,27 @@ class Mod {
|
|
|
1464
1530
|
* @returns `true` if the object is the same `Mod`, `false` otherwise.
|
|
1465
1531
|
*/
|
|
1466
1532
|
equals(other) {
|
|
1467
|
-
|
|
1533
|
+
if (this === other) {
|
|
1534
|
+
return true;
|
|
1535
|
+
}
|
|
1536
|
+
if (this.acronym !== other.acronym) {
|
|
1537
|
+
return false;
|
|
1538
|
+
}
|
|
1539
|
+
const settings = this.settings;
|
|
1540
|
+
const otherSettings = other.settings;
|
|
1541
|
+
if (settings.length !== otherSettings.length) {
|
|
1542
|
+
return false;
|
|
1543
|
+
}
|
|
1544
|
+
for (const setting of settings) {
|
|
1545
|
+
const otherSetting = otherSettings.find((s) => s.name === setting.name);
|
|
1546
|
+
if (!otherSetting) {
|
|
1547
|
+
return false;
|
|
1548
|
+
}
|
|
1549
|
+
if (setting.value !== otherSetting.value) {
|
|
1550
|
+
return false;
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
return true;
|
|
1468
1554
|
}
|
|
1469
1555
|
/**
|
|
1470
1556
|
* Returns the string representation of this `Mod`.
|
|
@@ -1475,329 +1561,105 @@ class Mod {
|
|
|
1475
1561
|
}
|
|
1476
1562
|
|
|
1477
1563
|
/**
|
|
1478
|
-
* Represents
|
|
1564
|
+
* Represents a hitobject that can be nested within a slider.
|
|
1479
1565
|
*/
|
|
1480
|
-
class
|
|
1481
|
-
constructor() {
|
|
1482
|
-
super();
|
|
1483
|
-
this.
|
|
1484
|
-
this.
|
|
1485
|
-
this.droidRanked = false;
|
|
1486
|
-
this.osuRanked = false;
|
|
1487
|
-
this.bitwise = 1 << 7;
|
|
1488
|
-
this.incompatibleMods.add(ModAuto).add(ModAutopilot);
|
|
1489
|
-
}
|
|
1490
|
-
get isDroidRelevant() {
|
|
1491
|
-
return true;
|
|
1492
|
-
}
|
|
1493
|
-
calculateDroidScoreMultiplier() {
|
|
1494
|
-
return 0.001;
|
|
1566
|
+
class SliderNestedHitObject extends HitObject {
|
|
1567
|
+
constructor(values) {
|
|
1568
|
+
super(values);
|
|
1569
|
+
this.spanIndex = values.spanIndex;
|
|
1570
|
+
this.spanStartTime = values.spanStartTime;
|
|
1495
1571
|
}
|
|
1496
|
-
|
|
1497
|
-
return
|
|
1572
|
+
toString() {
|
|
1573
|
+
return `Position: [${this._position.x}, ${this._position.y}], span index: ${this.spanIndex}, span start time: ${this.spanStartTime}`;
|
|
1498
1574
|
}
|
|
1499
|
-
|
|
1500
|
-
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
/**
|
|
1578
|
+
* Represents the head of a slider.
|
|
1579
|
+
*/
|
|
1580
|
+
class SliderHead extends SliderNestedHitObject {
|
|
1581
|
+
constructor(values) {
|
|
1582
|
+
super(Object.assign(Object.assign({}, values), { spanIndex: 0, spanStartTime: values.startTime }));
|
|
1501
1583
|
}
|
|
1502
1584
|
}
|
|
1503
1585
|
|
|
1504
1586
|
/**
|
|
1505
|
-
*
|
|
1587
|
+
* An empty `HitWindow` that does not have any hit windows.
|
|
1588
|
+
*
|
|
1589
|
+
* No time values are provided (meaning instantaneous hit or miss).
|
|
1506
1590
|
*/
|
|
1507
|
-
class
|
|
1591
|
+
class EmptyHitWindow extends HitWindow {
|
|
1508
1592
|
constructor() {
|
|
1509
|
-
super();
|
|
1510
|
-
this.acronym = "AP";
|
|
1511
|
-
this.name = "Autopilot";
|
|
1512
|
-
this.droidRanked = false;
|
|
1513
|
-
this.osuRanked = false;
|
|
1514
|
-
this.bitwise = 1 << 13;
|
|
1515
|
-
this.incompatibleMods.add(ModRelax).add(ModAuto);
|
|
1516
|
-
}
|
|
1517
|
-
get isDroidRelevant() {
|
|
1518
|
-
return true;
|
|
1593
|
+
super(0);
|
|
1519
1594
|
}
|
|
1520
|
-
|
|
1521
|
-
return 0
|
|
1595
|
+
get greatWindow() {
|
|
1596
|
+
return 0;
|
|
1522
1597
|
}
|
|
1523
|
-
get
|
|
1524
|
-
return
|
|
1598
|
+
get okWindow() {
|
|
1599
|
+
return 0;
|
|
1525
1600
|
}
|
|
1526
|
-
get
|
|
1601
|
+
get mehWindow() {
|
|
1527
1602
|
return 0;
|
|
1528
1603
|
}
|
|
1529
1604
|
}
|
|
1530
1605
|
|
|
1531
1606
|
/**
|
|
1532
|
-
* Represents the
|
|
1607
|
+
* Represents a nested hit object that is at the end of a slider path (either repeat or tail).
|
|
1533
1608
|
*/
|
|
1534
|
-
class
|
|
1535
|
-
constructor() {
|
|
1536
|
-
super();
|
|
1537
|
-
this.
|
|
1538
|
-
this.
|
|
1539
|
-
this.droidRanked = false;
|
|
1540
|
-
this.osuRanked = false;
|
|
1541
|
-
this.bitwise = 1 << 11;
|
|
1542
|
-
this.incompatibleMods.add(ModAutopilot).add(ModRelax);
|
|
1543
|
-
}
|
|
1544
|
-
get isDroidRelevant() {
|
|
1545
|
-
return true;
|
|
1546
|
-
}
|
|
1547
|
-
calculateDroidScoreMultiplier() {
|
|
1548
|
-
return 1;
|
|
1609
|
+
class SliderEndCircle extends SliderNestedHitObject {
|
|
1610
|
+
constructor(values) {
|
|
1611
|
+
super(values);
|
|
1612
|
+
this.sliderSpanDuration = values.sliderSpanDuration;
|
|
1613
|
+
this.sliderStartTime = values.sliderStartTime;
|
|
1549
1614
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1615
|
+
applyDefaults(controlPoints, difficulty, mode) {
|
|
1616
|
+
super.applyDefaults(controlPoints, difficulty, mode);
|
|
1617
|
+
if (this.spanIndex > 0) {
|
|
1618
|
+
// Repeat points after the first span should appear behind the still-visible one.
|
|
1619
|
+
this.timeFadeIn = 0;
|
|
1620
|
+
// The next end circle should appear exactly after the previous circle (on the same end) is hit.
|
|
1621
|
+
this.timePreempt = this.sliderSpanDuration * 2;
|
|
1622
|
+
}
|
|
1623
|
+
else {
|
|
1624
|
+
// The first end circle should fade in with the slider.
|
|
1625
|
+
this.timePreempt += this.startTime - this.sliderStartTime;
|
|
1626
|
+
}
|
|
1552
1627
|
}
|
|
1553
|
-
|
|
1554
|
-
return
|
|
1628
|
+
createHitWindow() {
|
|
1629
|
+
return new EmptyHitWindow();
|
|
1555
1630
|
}
|
|
1556
1631
|
}
|
|
1557
1632
|
|
|
1558
1633
|
/**
|
|
1559
|
-
* Represents a
|
|
1634
|
+
* Represents a slider repeat.
|
|
1560
1635
|
*/
|
|
1561
|
-
class
|
|
1562
|
-
get value() {
|
|
1563
|
-
return super.value;
|
|
1564
|
-
}
|
|
1565
|
-
set value(value) {
|
|
1566
|
-
super.value = this.processValue(value);
|
|
1567
|
-
}
|
|
1568
|
-
constructor(name, description, defaultValue, min, max, step) {
|
|
1569
|
-
super(name, description, defaultValue);
|
|
1570
|
-
this.min = min;
|
|
1571
|
-
this.max = max;
|
|
1572
|
-
this.step = step;
|
|
1573
|
-
}
|
|
1636
|
+
class SliderRepeat extends SliderEndCircle {
|
|
1574
1637
|
}
|
|
1575
1638
|
|
|
1576
1639
|
/**
|
|
1577
|
-
* Represents a
|
|
1640
|
+
* Represents a slider tick in a slider.
|
|
1578
1641
|
*/
|
|
1579
|
-
class
|
|
1580
|
-
|
|
1581
|
-
super(
|
|
1582
|
-
|
|
1583
|
-
if (
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
throw new RangeError(`The step size (${step}) must be greater than or equal to 0.`);
|
|
1642
|
+
class SliderTick extends SliderNestedHitObject {
|
|
1643
|
+
applyDefaults(controlPoints, difficulty, mode) {
|
|
1644
|
+
super.applyDefaults(controlPoints, difficulty, mode);
|
|
1645
|
+
let offset;
|
|
1646
|
+
if (this.spanIndex > 0) {
|
|
1647
|
+
// Adding 200 to include the offset stable used.
|
|
1648
|
+
// This is so on repeats ticks don't appear too late to be visually processed by the player.
|
|
1649
|
+
offset = 200;
|
|
1588
1650
|
}
|
|
1589
|
-
|
|
1590
|
-
|
|
1651
|
+
else {
|
|
1652
|
+
offset = this.timePreempt * 0.66;
|
|
1591
1653
|
}
|
|
1654
|
+
this.timePreempt = (this.startTime - this.spanStartTime) / 2 + offset;
|
|
1592
1655
|
}
|
|
1593
|
-
|
|
1594
|
-
return
|
|
1656
|
+
createHitWindow() {
|
|
1657
|
+
return new EmptyHitWindow();
|
|
1595
1658
|
}
|
|
1596
1659
|
}
|
|
1597
1660
|
|
|
1598
1661
|
/**
|
|
1599
|
-
* Represents
|
|
1600
|
-
*/
|
|
1601
|
-
class DecimalModSetting extends NumberModSetting {
|
|
1602
|
-
constructor(name, description, defaultValue, min = -Number.MAX_VALUE, max = Number.MAX_VALUE, step = 0, precision = null) {
|
|
1603
|
-
super(name, description, defaultValue, min, max, step);
|
|
1604
|
-
this.displayFormatter = (v) => {
|
|
1605
|
-
if (this.precision !== null) {
|
|
1606
|
-
return v.toFixed(this.precision);
|
|
1607
|
-
}
|
|
1608
|
-
return super.toDisplayString();
|
|
1609
|
-
};
|
|
1610
|
-
if (precision !== null && precision < 0) {
|
|
1611
|
-
throw new RangeError(`The precision (${precision}) must be greater than or equal to 0.`);
|
|
1612
|
-
}
|
|
1613
|
-
this.precision = precision;
|
|
1614
|
-
}
|
|
1615
|
-
processValue(value) {
|
|
1616
|
-
const processedValue = super.processValue(value);
|
|
1617
|
-
if (this.precision !== null) {
|
|
1618
|
-
return parseFloat(processedValue.toFixed(this.precision));
|
|
1619
|
-
}
|
|
1620
|
-
return processedValue;
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
|
|
1624
|
-
/**
|
|
1625
|
-
* Represents a `Mod` that adjusts the playback rate of a track.
|
|
1626
|
-
*/
|
|
1627
|
-
class ModRateAdjust extends Mod {
|
|
1628
|
-
/**
|
|
1629
|
-
* The generic osu!droid score multiplier of this `Mod`.
|
|
1630
|
-
*/
|
|
1631
|
-
get droidScoreMultiplier() {
|
|
1632
|
-
return this.trackRateMultiplier.value >= 1
|
|
1633
|
-
? 1 + (this.trackRateMultiplier.value - 1) * 0.24
|
|
1634
|
-
: Math.pow(0.3, (1 - this.trackRateMultiplier.value) * 4);
|
|
1635
|
-
}
|
|
1636
|
-
/**
|
|
1637
|
-
* Generic getter to determine if this `ModRateAdjust` is relevant.
|
|
1638
|
-
*/
|
|
1639
|
-
get isRelevant() {
|
|
1640
|
-
return this.trackRateMultiplier.value !== 1;
|
|
1641
|
-
}
|
|
1642
|
-
constructor(trackRateMultiplier = 1) {
|
|
1643
|
-
super();
|
|
1644
|
-
this.trackRateMultiplier = new DecimalModSetting("Track rate multiplier", "The multiplier for the track's playback rate after applying this mod.", trackRateMultiplier, 0.5, 2, 0.05, 2);
|
|
1645
|
-
}
|
|
1646
|
-
applyToRate(_, rate) {
|
|
1647
|
-
return rate * this.trackRateMultiplier.value;
|
|
1648
|
-
}
|
|
1649
|
-
equals(other) {
|
|
1650
|
-
return (super.equals(other) &&
|
|
1651
|
-
other instanceof ModRateAdjust &&
|
|
1652
|
-
this.trackRateMultiplier.value === other.trackRateMultiplier.value);
|
|
1653
|
-
}
|
|
1654
|
-
}
|
|
1655
|
-
|
|
1656
|
-
/**
|
|
1657
|
-
* Represents the Custom Speed mod.
|
|
1658
|
-
*
|
|
1659
|
-
* This is a replacement `Mod` for speed modify in osu!droid and custom rates in osu!lazer.
|
|
1660
|
-
*/
|
|
1661
|
-
class ModCustomSpeed extends ModRateAdjust {
|
|
1662
|
-
constructor() {
|
|
1663
|
-
super(...arguments);
|
|
1664
|
-
this.acronym = "CS";
|
|
1665
|
-
this.name = "Custom Speed";
|
|
1666
|
-
this.droidRanked = true;
|
|
1667
|
-
this.osuRanked = false;
|
|
1668
|
-
}
|
|
1669
|
-
copySettings(mod) {
|
|
1670
|
-
var _a, _b;
|
|
1671
|
-
super.copySettings(mod);
|
|
1672
|
-
this.trackRateMultiplier.value =
|
|
1673
|
-
(_b = (_a = mod.settings) === null || _a === void 0 ? void 0 : _a.rateMultiplier) !== null && _b !== void 0 ? _b : this.trackRateMultiplier.value;
|
|
1674
|
-
}
|
|
1675
|
-
get isDroidRelevant() {
|
|
1676
|
-
return this.isRelevant;
|
|
1677
|
-
}
|
|
1678
|
-
calculateDroidScoreMultiplier() {
|
|
1679
|
-
return this.droidScoreMultiplier;
|
|
1680
|
-
}
|
|
1681
|
-
get isOsuRelevant() {
|
|
1682
|
-
return this.isRelevant;
|
|
1683
|
-
}
|
|
1684
|
-
get osuScoreMultiplier() {
|
|
1685
|
-
// Round to the nearest multiple of 0.1.
|
|
1686
|
-
let value = Math.trunc(this.trackRateMultiplier.value * 10) / 10;
|
|
1687
|
-
// Offset back to 0.
|
|
1688
|
-
--value;
|
|
1689
|
-
return this.trackRateMultiplier.value >= 1
|
|
1690
|
-
? 1 + value / 5
|
|
1691
|
-
: 0.6 + value;
|
|
1692
|
-
}
|
|
1693
|
-
serializeSettings() {
|
|
1694
|
-
return { rateMultiplier: this.trackRateMultiplier.value };
|
|
1695
|
-
}
|
|
1696
|
-
toString() {
|
|
1697
|
-
return `${super.toString()} (${this.trackRateMultiplier.toDisplayString()}x)`;
|
|
1698
|
-
}
|
|
1699
|
-
}
|
|
1700
|
-
|
|
1701
|
-
/**
|
|
1702
|
-
* Represents a hitobject that can be nested within a slider.
|
|
1703
|
-
*/
|
|
1704
|
-
class SliderNestedHitObject extends HitObject {
|
|
1705
|
-
constructor(values) {
|
|
1706
|
-
super(values);
|
|
1707
|
-
this.spanIndex = values.spanIndex;
|
|
1708
|
-
this.spanStartTime = values.spanStartTime;
|
|
1709
|
-
}
|
|
1710
|
-
toString() {
|
|
1711
|
-
return `Position: [${this._position.x}, ${this._position.y}], span index: ${this.spanIndex}, span start time: ${this.spanStartTime}`;
|
|
1712
|
-
}
|
|
1713
|
-
}
|
|
1714
|
-
|
|
1715
|
-
/**
|
|
1716
|
-
* Represents the head of a slider.
|
|
1717
|
-
*/
|
|
1718
|
-
class SliderHead extends SliderNestedHitObject {
|
|
1719
|
-
constructor(values) {
|
|
1720
|
-
super(Object.assign(Object.assign({}, values), { spanIndex: 0, spanStartTime: values.startTime }));
|
|
1721
|
-
}
|
|
1722
|
-
}
|
|
1723
|
-
|
|
1724
|
-
/**
|
|
1725
|
-
* An empty `HitWindow` that does not have any hit windows.
|
|
1726
|
-
*
|
|
1727
|
-
* No time values are provided (meaning instantaneous hit or miss).
|
|
1728
|
-
*/
|
|
1729
|
-
class EmptyHitWindow extends HitWindow {
|
|
1730
|
-
constructor() {
|
|
1731
|
-
super(0);
|
|
1732
|
-
}
|
|
1733
|
-
get greatWindow() {
|
|
1734
|
-
return 0;
|
|
1735
|
-
}
|
|
1736
|
-
get okWindow() {
|
|
1737
|
-
return 0;
|
|
1738
|
-
}
|
|
1739
|
-
get mehWindow() {
|
|
1740
|
-
return 0;
|
|
1741
|
-
}
|
|
1742
|
-
}
|
|
1743
|
-
|
|
1744
|
-
/**
|
|
1745
|
-
* Represents a nested hit object that is at the end of a slider path (either repeat or tail).
|
|
1746
|
-
*/
|
|
1747
|
-
class SliderEndCircle extends SliderNestedHitObject {
|
|
1748
|
-
constructor(values) {
|
|
1749
|
-
super(values);
|
|
1750
|
-
this.sliderSpanDuration = values.sliderSpanDuration;
|
|
1751
|
-
this.sliderStartTime = values.sliderStartTime;
|
|
1752
|
-
}
|
|
1753
|
-
applyDefaults(controlPoints, difficulty, mode) {
|
|
1754
|
-
super.applyDefaults(controlPoints, difficulty, mode);
|
|
1755
|
-
if (this.spanIndex > 0) {
|
|
1756
|
-
// Repeat points after the first span should appear behind the still-visible one.
|
|
1757
|
-
this.timeFadeIn = 0;
|
|
1758
|
-
// The next end circle should appear exactly after the previous circle (on the same end) is hit.
|
|
1759
|
-
this.timePreempt = this.sliderSpanDuration * 2;
|
|
1760
|
-
}
|
|
1761
|
-
else {
|
|
1762
|
-
// The first end circle should fade in with the slider.
|
|
1763
|
-
this.timePreempt += this.startTime - this.sliderStartTime;
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
createHitWindow() {
|
|
1767
|
-
return new EmptyHitWindow();
|
|
1768
|
-
}
|
|
1769
|
-
}
|
|
1770
|
-
|
|
1771
|
-
/**
|
|
1772
|
-
* Represents a slider repeat.
|
|
1773
|
-
*/
|
|
1774
|
-
class SliderRepeat extends SliderEndCircle {
|
|
1775
|
-
}
|
|
1776
|
-
|
|
1777
|
-
/**
|
|
1778
|
-
* Represents a slider tick in a slider.
|
|
1779
|
-
*/
|
|
1780
|
-
class SliderTick extends SliderNestedHitObject {
|
|
1781
|
-
applyDefaults(controlPoints, difficulty, mode) {
|
|
1782
|
-
super.applyDefaults(controlPoints, difficulty, mode);
|
|
1783
|
-
let offset;
|
|
1784
|
-
if (this.spanIndex > 0) {
|
|
1785
|
-
// Adding 200 to include the offset stable used.
|
|
1786
|
-
// This is so on repeats ticks don't appear too late to be visually processed by the player.
|
|
1787
|
-
offset = 200;
|
|
1788
|
-
}
|
|
1789
|
-
else {
|
|
1790
|
-
offset = this.timePreempt * 0.66;
|
|
1791
|
-
}
|
|
1792
|
-
this.timePreempt = (this.startTime - this.spanStartTime) / 2 + offset;
|
|
1793
|
-
}
|
|
1794
|
-
createHitWindow() {
|
|
1795
|
-
return new EmptyHitWindow();
|
|
1796
|
-
}
|
|
1797
|
-
}
|
|
1798
|
-
|
|
1799
|
-
/**
|
|
1800
|
-
* Represents the tail of a slider.
|
|
1662
|
+
* Represents the tail of a slider.
|
|
1801
1663
|
*/
|
|
1802
1664
|
class SliderTail extends SliderEndCircle {
|
|
1803
1665
|
}
|
|
@@ -2297,16 +2159,654 @@ class Slider extends HitObject {
|
|
|
2297
2159
|
default:
|
|
2298
2160
|
v.samples.push(...sampleList);
|
|
2299
2161
|
}
|
|
2300
|
-
});
|
|
2162
|
+
});
|
|
2163
|
+
}
|
|
2164
|
+
toString() {
|
|
2165
|
+
return `Position: [${this.position.x}, ${this.position.y}], distance: ${this.path.expectedDistance}, repeat count: ${this.repeatCount}, slider ticks: ${this.nestedHitObjects.filter((v) => v instanceof SliderTick).length}`;
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
Slider.baseNormalSlideSample = new BankHitSampleInfo("sliderslide");
|
|
2169
|
+
Slider.baseWhistleSlideSample = new BankHitSampleInfo("sliderwhistle");
|
|
2170
|
+
Slider.baseTickSample = new BankHitSampleInfo("slidertick");
|
|
2171
|
+
Slider.legacyLastTickOffset = 36;
|
|
2172
|
+
|
|
2173
|
+
/**
|
|
2174
|
+
* Represents the Freeze Frame mod.
|
|
2175
|
+
*/
|
|
2176
|
+
class ModFreezeFrame extends Mod {
|
|
2177
|
+
constructor() {
|
|
2178
|
+
super();
|
|
2179
|
+
this.name = "Freeze Frame";
|
|
2180
|
+
this.acronym = "FR";
|
|
2181
|
+
this.lastNewComboTime = 0;
|
|
2182
|
+
this.incompatibleMods.add(ModApproachDifferent);
|
|
2183
|
+
}
|
|
2184
|
+
get droidRanked() {
|
|
2185
|
+
return false;
|
|
2186
|
+
}
|
|
2187
|
+
get isDroidRelevant() {
|
|
2188
|
+
return true;
|
|
2189
|
+
}
|
|
2190
|
+
calculateDroidScoreMultiplier() {
|
|
2191
|
+
return 1;
|
|
2192
|
+
}
|
|
2193
|
+
get osuRanked() {
|
|
2194
|
+
return false;
|
|
2195
|
+
}
|
|
2196
|
+
get isOsuRelevant() {
|
|
2197
|
+
return true;
|
|
2198
|
+
}
|
|
2199
|
+
get osuScoreMultiplier() {
|
|
2200
|
+
return 1;
|
|
2201
|
+
}
|
|
2202
|
+
applyToBeatmap(beatmap) {
|
|
2203
|
+
this.lastNewComboTime = 0;
|
|
2204
|
+
for (const hitObject of beatmap.hitObjects) {
|
|
2205
|
+
if (hitObject.isNewCombo) {
|
|
2206
|
+
this.lastNewComboTime = hitObject.startTime;
|
|
2207
|
+
}
|
|
2208
|
+
this.applyFadeInAdjustment(hitObject);
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
applyFadeInAdjustment(hitObject) {
|
|
2212
|
+
hitObject.timePreempt += hitObject.startTime - this.lastNewComboTime;
|
|
2213
|
+
if (hitObject instanceof Slider) {
|
|
2214
|
+
// Freezing slider ticks doesn't play well with snaking sliders, and slider repeats will not
|
|
2215
|
+
// layer correctly if its preempt is changed.
|
|
2216
|
+
this.applyFadeInAdjustment(hitObject.head);
|
|
2217
|
+
this.applyFadeInAdjustment(hitObject.tail);
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
/**
|
|
2223
|
+
* Represents the Traceable mod.
|
|
2224
|
+
*/
|
|
2225
|
+
class ModTraceable extends Mod {
|
|
2226
|
+
constructor() {
|
|
2227
|
+
super();
|
|
2228
|
+
this.acronym = "TC";
|
|
2229
|
+
this.name = "Traceable";
|
|
2230
|
+
this.droidRanked = false;
|
|
2231
|
+
this.osuRanked = false;
|
|
2232
|
+
this.incompatibleMods.add(ModHidden);
|
|
2233
|
+
}
|
|
2234
|
+
get isDroidRelevant() {
|
|
2235
|
+
return true;
|
|
2236
|
+
}
|
|
2237
|
+
calculateDroidScoreMultiplier() {
|
|
2238
|
+
return 1.06;
|
|
2239
|
+
}
|
|
2240
|
+
get isOsuRelevant() {
|
|
2241
|
+
return true;
|
|
2242
|
+
}
|
|
2243
|
+
get osuScoreMultiplier() {
|
|
2244
|
+
return 1;
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
/**
|
|
2249
|
+
* Represents a `Mod` specific setting that is constrained to a boolean value.
|
|
2250
|
+
*/
|
|
2251
|
+
class BooleanModSetting extends ModSetting {
|
|
2252
|
+
constructor() {
|
|
2253
|
+
super(...arguments);
|
|
2254
|
+
this.displayFormatter = (v) => v ? "Enabled" : "Disabled";
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
/**
|
|
2259
|
+
* Represents the Hidden mod.
|
|
2260
|
+
*/
|
|
2261
|
+
class ModHidden extends Mod {
|
|
2262
|
+
get droidRanked() {
|
|
2263
|
+
return this.usesDefaultSettings;
|
|
2264
|
+
}
|
|
2265
|
+
get osuRanked() {
|
|
2266
|
+
return this.usesDefaultSettings;
|
|
2267
|
+
}
|
|
2268
|
+
constructor() {
|
|
2269
|
+
super();
|
|
2270
|
+
this.acronym = "HD";
|
|
2271
|
+
this.name = "Hidden";
|
|
2272
|
+
this.bitwise = 1 << 3;
|
|
2273
|
+
/**
|
|
2274
|
+
* Whether to only fade approach circles.
|
|
2275
|
+
*
|
|
2276
|
+
* The main object body will not fade when enabled.
|
|
2277
|
+
*/
|
|
2278
|
+
this.onlyFadeApproachCircles = new BooleanModSetting("Only fade approach circles", "The main object body will not fade when enabled.", false);
|
|
2279
|
+
this.incompatibleMods.add(ModTraceable).add(ModApproachDifferent);
|
|
2280
|
+
}
|
|
2281
|
+
get isDroidRelevant() {
|
|
2282
|
+
return true;
|
|
2283
|
+
}
|
|
2284
|
+
calculateDroidScoreMultiplier() {
|
|
2285
|
+
return 1.06;
|
|
2286
|
+
}
|
|
2287
|
+
get isOsuRelevant() {
|
|
2288
|
+
return true;
|
|
2289
|
+
}
|
|
2290
|
+
get osuScoreMultiplier() {
|
|
2291
|
+
return 1.06;
|
|
2292
|
+
}
|
|
2293
|
+
copySettings(mod) {
|
|
2294
|
+
var _a, _b;
|
|
2295
|
+
super.copySettings(mod);
|
|
2296
|
+
this.onlyFadeApproachCircles.value =
|
|
2297
|
+
(_b = (_a = mod.settings) === null || _a === void 0 ? void 0 : _a.onlyFadeApproachCircles) !== null && _b !== void 0 ? _b : this.onlyFadeApproachCircles.value;
|
|
2298
|
+
}
|
|
2299
|
+
applyToBeatmap(beatmap) {
|
|
2300
|
+
const applyFadeInAdjustment = (hitObject) => {
|
|
2301
|
+
hitObject.timeFadeIn =
|
|
2302
|
+
hitObject.timePreempt * ModHidden.fadeInDurationMultiplier;
|
|
2303
|
+
if (hitObject instanceof Slider) {
|
|
2304
|
+
hitObject.nestedHitObjects.forEach(applyFadeInAdjustment);
|
|
2305
|
+
}
|
|
2306
|
+
};
|
|
2307
|
+
beatmap.hitObjects.objects.forEach(applyFadeInAdjustment);
|
|
2308
|
+
}
|
|
2309
|
+
serializeSettings() {
|
|
2310
|
+
return this.onlyFadeApproachCircles.value
|
|
2311
|
+
? { onlyFadeApproachCircles: this.onlyFadeApproachCircles.value }
|
|
2312
|
+
: null;
|
|
2313
|
+
}
|
|
2314
|
+
toString() {
|
|
2315
|
+
if (!this.onlyFadeApproachCircles.value) {
|
|
2316
|
+
return super.toString();
|
|
2317
|
+
}
|
|
2318
|
+
return `${super.toString()} (approach circles only)`;
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
ModHidden.fadeInDurationMultiplier = 0.4;
|
|
2322
|
+
ModHidden.fadeOutDurationMultiplier = 0.3;
|
|
2323
|
+
|
|
2324
|
+
/**
|
|
2325
|
+
* Represents a `Mod` specific setting that is constrained to a range of values.
|
|
2326
|
+
*/
|
|
2327
|
+
class RangeConstrainedModSetting extends ModSetting {
|
|
2328
|
+
/**
|
|
2329
|
+
* The minimum value of this `RangeConstrainedModSetting`.
|
|
2330
|
+
*/
|
|
2331
|
+
get min() {
|
|
2332
|
+
return this._min;
|
|
2333
|
+
}
|
|
2334
|
+
set min(value) {
|
|
2335
|
+
this._min = value;
|
|
2336
|
+
this.value = this.processValue(this.value);
|
|
2337
|
+
}
|
|
2338
|
+
/**
|
|
2339
|
+
* The maximum value of this `RangeConstrainedModSetting`.
|
|
2340
|
+
*/
|
|
2341
|
+
get max() {
|
|
2342
|
+
return this._max;
|
|
2343
|
+
}
|
|
2344
|
+
set max(value) {
|
|
2345
|
+
this._max = value;
|
|
2346
|
+
this.value = this.processValue(this.value);
|
|
2347
|
+
}
|
|
2348
|
+
/**
|
|
2349
|
+
* The step size of this `RangeConstrainedModSetting`.
|
|
2350
|
+
*/
|
|
2351
|
+
get step() {
|
|
2352
|
+
return this._step;
|
|
2353
|
+
}
|
|
2354
|
+
set step(value) {
|
|
2355
|
+
this._step = value;
|
|
2356
|
+
this.value = this.processValue(this.value);
|
|
2357
|
+
}
|
|
2358
|
+
get value() {
|
|
2359
|
+
return super.value;
|
|
2360
|
+
}
|
|
2361
|
+
set value(value) {
|
|
2362
|
+
super.value = this.processValue(value);
|
|
2363
|
+
}
|
|
2364
|
+
constructor(name, description, defaultValue, min, max, step) {
|
|
2365
|
+
super(name, description, defaultValue);
|
|
2366
|
+
this._min = min;
|
|
2367
|
+
this._max = max;
|
|
2368
|
+
this._step = step;
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
/**
|
|
2373
|
+
* Represents a `Mod` specific setting that is constrained to a number of values.
|
|
2374
|
+
*/
|
|
2375
|
+
class NumberModSetting extends RangeConstrainedModSetting {
|
|
2376
|
+
constructor(name, description, defaultValue, min, max, step) {
|
|
2377
|
+
super(name, description, defaultValue, min, max, step);
|
|
2378
|
+
this.displayFormatter = (v) => v.toString();
|
|
2379
|
+
if (min > max) {
|
|
2380
|
+
throw new RangeError(`The minimum value (${min}) must be less than or equal to the maximum value (${max}).`);
|
|
2381
|
+
}
|
|
2382
|
+
if (step < 0) {
|
|
2383
|
+
throw new RangeError(`The step size (${step}) must be greater than or equal to 0.`);
|
|
2384
|
+
}
|
|
2385
|
+
if (defaultValue < min || defaultValue > max) {
|
|
2386
|
+
throw new RangeError(`The default value (${defaultValue}) must be between the minimum (${min}) and maximum (${max}) values.`);
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
processValue(value) {
|
|
2390
|
+
return MathUtils.clamp(Math.round(value / this.step) * this.step, this.min, this.max);
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
/**
|
|
2395
|
+
* Represents a `Mod` specific setting that is constrained to a range of decimal values.
|
|
2396
|
+
*/
|
|
2397
|
+
class DecimalModSetting extends NumberModSetting {
|
|
2398
|
+
/**
|
|
2399
|
+
* The number of decimal places to round the value to.
|
|
2400
|
+
*
|
|
2401
|
+
* When set to `null`, the value will not be rounded.
|
|
2402
|
+
*/
|
|
2403
|
+
get precision() {
|
|
2404
|
+
return this._precision;
|
|
2405
|
+
}
|
|
2406
|
+
set precision(value) {
|
|
2407
|
+
if (value !== null && value < 0) {
|
|
2408
|
+
throw new RangeError(`The precision (${value}) must be greater than or equal to 0.`);
|
|
2409
|
+
}
|
|
2410
|
+
this._precision = value;
|
|
2411
|
+
if (value !== null) {
|
|
2412
|
+
this.value = this.processValue(this.value);
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
constructor(name, description, defaultValue, min = -Number.MAX_VALUE, max = Number.MAX_VALUE, step = 0, precision = null) {
|
|
2416
|
+
super(name, description, defaultValue, min, max, step);
|
|
2417
|
+
this.displayFormatter = (v) => {
|
|
2418
|
+
if (this.precision !== null) {
|
|
2419
|
+
return v.toFixed(this.precision);
|
|
2420
|
+
}
|
|
2421
|
+
return super.toDisplayString();
|
|
2422
|
+
};
|
|
2423
|
+
if (precision !== null && precision < 0) {
|
|
2424
|
+
throw new RangeError(`The precision (${precision}) must be greater than or equal to 0.`);
|
|
2425
|
+
}
|
|
2426
|
+
this._precision = precision;
|
|
2427
|
+
}
|
|
2428
|
+
processValue(value) {
|
|
2429
|
+
const processedValue = super.processValue(value);
|
|
2430
|
+
if (this.precision !== null) {
|
|
2431
|
+
return parseFloat(processedValue.toFixed(this.precision));
|
|
2432
|
+
}
|
|
2433
|
+
return processedValue;
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
/**
|
|
2438
|
+
* Represents the Approach Different mod.
|
|
2439
|
+
*/
|
|
2440
|
+
class ModApproachDifferent extends Mod {
|
|
2441
|
+
/**
|
|
2442
|
+
* The {@link Easing} to apply to the approach circle animation.
|
|
2443
|
+
*/
|
|
2444
|
+
get easing() {
|
|
2445
|
+
switch (this.style.value) {
|
|
2446
|
+
case exports.AnimationStyle.linear:
|
|
2447
|
+
return exports.Easing.none;
|
|
2448
|
+
case exports.AnimationStyle.gravity:
|
|
2449
|
+
return exports.Easing.inBack;
|
|
2450
|
+
case exports.AnimationStyle.inOut1:
|
|
2451
|
+
return exports.Easing.inOutCubic;
|
|
2452
|
+
case exports.AnimationStyle.inOut2:
|
|
2453
|
+
return exports.Easing.inOutQuint;
|
|
2454
|
+
case exports.AnimationStyle.accelerate1:
|
|
2455
|
+
return exports.Easing.in;
|
|
2456
|
+
case exports.AnimationStyle.accelerate2:
|
|
2457
|
+
return exports.Easing.inCubic;
|
|
2458
|
+
case exports.AnimationStyle.accelerate3:
|
|
2459
|
+
return exports.Easing.inQuint;
|
|
2460
|
+
case exports.AnimationStyle.decelerate1:
|
|
2461
|
+
return exports.Easing.out;
|
|
2462
|
+
case exports.AnimationStyle.decelerate2:
|
|
2463
|
+
return exports.Easing.outCubic;
|
|
2464
|
+
case exports.AnimationStyle.decelerate3:
|
|
2465
|
+
return exports.Easing.outQuint;
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
constructor() {
|
|
2469
|
+
super();
|
|
2470
|
+
this.name = "Approach Different";
|
|
2471
|
+
this.acronym = "AD";
|
|
2472
|
+
/**
|
|
2473
|
+
* The initial size of the approach circle, relative to hit circles.
|
|
2474
|
+
*/
|
|
2475
|
+
this.scale = new DecimalModSetting("Initial size", "The initial size of the approach circle, relative to hit circles.", 3, 1.5, 10, 0.1, 1);
|
|
2476
|
+
/**
|
|
2477
|
+
* The animation style of the approach circles.
|
|
2478
|
+
*/
|
|
2479
|
+
this.style = new ModSetting("Style", "The animation style of the approach circles.", exports.AnimationStyle.gravity);
|
|
2480
|
+
this.incompatibleMods.add(ModHidden).add(ModFreezeFrame);
|
|
2481
|
+
}
|
|
2482
|
+
get droidRanked() {
|
|
2483
|
+
return false;
|
|
2484
|
+
}
|
|
2485
|
+
get isDroidRelevant() {
|
|
2486
|
+
return true;
|
|
2487
|
+
}
|
|
2488
|
+
calculateDroidScoreMultiplier() {
|
|
2489
|
+
return 1;
|
|
2490
|
+
}
|
|
2491
|
+
get osuRanked() {
|
|
2492
|
+
return false;
|
|
2493
|
+
}
|
|
2494
|
+
get isOsuRelevant() {
|
|
2495
|
+
return true;
|
|
2496
|
+
}
|
|
2497
|
+
get osuScoreMultiplier() {
|
|
2498
|
+
return 1;
|
|
2499
|
+
}
|
|
2500
|
+
copySettings(mod) {
|
|
2501
|
+
super.copySettings(mod);
|
|
2502
|
+
const { settings } = mod;
|
|
2503
|
+
if (typeof (settings === null || settings === void 0 ? void 0 : settings.scale) === "number") {
|
|
2504
|
+
this.scale.value = settings.scale;
|
|
2505
|
+
}
|
|
2506
|
+
if (typeof (settings === null || settings === void 0 ? void 0 : settings.style) === "number") {
|
|
2507
|
+
this.style.value = settings.style;
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
serializeSettings() {
|
|
2511
|
+
return {
|
|
2512
|
+
scale: this.scale.value,
|
|
2513
|
+
style: this.style.value,
|
|
2514
|
+
};
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
exports.AnimationStyle = void 0;
|
|
2518
|
+
(function (AnimationStyle) {
|
|
2519
|
+
AnimationStyle[AnimationStyle["linear"] = 0] = "linear";
|
|
2520
|
+
AnimationStyle[AnimationStyle["gravity"] = 1] = "gravity";
|
|
2521
|
+
AnimationStyle[AnimationStyle["inOut1"] = 2] = "inOut1";
|
|
2522
|
+
AnimationStyle[AnimationStyle["inOut2"] = 3] = "inOut2";
|
|
2523
|
+
AnimationStyle[AnimationStyle["accelerate1"] = 4] = "accelerate1";
|
|
2524
|
+
AnimationStyle[AnimationStyle["accelerate2"] = 5] = "accelerate2";
|
|
2525
|
+
AnimationStyle[AnimationStyle["accelerate3"] = 6] = "accelerate3";
|
|
2526
|
+
AnimationStyle[AnimationStyle["decelerate1"] = 7] = "decelerate1";
|
|
2527
|
+
AnimationStyle[AnimationStyle["decelerate2"] = 8] = "decelerate2";
|
|
2528
|
+
AnimationStyle[AnimationStyle["decelerate3"] = 9] = "decelerate3";
|
|
2529
|
+
})(exports.AnimationStyle || (exports.AnimationStyle = {}));
|
|
2530
|
+
|
|
2531
|
+
/**
|
|
2532
|
+
* Represents the Relax mod.
|
|
2533
|
+
*/
|
|
2534
|
+
class ModRelax extends Mod {
|
|
2535
|
+
constructor() {
|
|
2536
|
+
super();
|
|
2537
|
+
this.acronym = "RX";
|
|
2538
|
+
this.name = "Relax";
|
|
2539
|
+
this.droidRanked = false;
|
|
2540
|
+
this.osuRanked = false;
|
|
2541
|
+
this.bitwise = 1 << 7;
|
|
2542
|
+
this.incompatibleMods.add(ModAuto).add(ModAutopilot);
|
|
2543
|
+
}
|
|
2544
|
+
get isDroidRelevant() {
|
|
2545
|
+
return true;
|
|
2546
|
+
}
|
|
2547
|
+
calculateDroidScoreMultiplier() {
|
|
2548
|
+
return 0.001;
|
|
2549
|
+
}
|
|
2550
|
+
get isOsuRelevant() {
|
|
2551
|
+
return true;
|
|
2552
|
+
}
|
|
2553
|
+
get osuScoreMultiplier() {
|
|
2554
|
+
return 0;
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
/**
|
|
2559
|
+
* Represents the Autopilot mod.
|
|
2560
|
+
*/
|
|
2561
|
+
class ModAutopilot extends Mod {
|
|
2562
|
+
constructor() {
|
|
2563
|
+
super();
|
|
2564
|
+
this.acronym = "AP";
|
|
2565
|
+
this.name = "Autopilot";
|
|
2566
|
+
this.droidRanked = false;
|
|
2567
|
+
this.osuRanked = false;
|
|
2568
|
+
this.bitwise = 1 << 13;
|
|
2569
|
+
this.incompatibleMods.add(ModRelax).add(ModAuto);
|
|
2570
|
+
}
|
|
2571
|
+
get isDroidRelevant() {
|
|
2572
|
+
return true;
|
|
2573
|
+
}
|
|
2574
|
+
calculateDroidScoreMultiplier() {
|
|
2575
|
+
return 0.001;
|
|
2576
|
+
}
|
|
2577
|
+
get isOsuRelevant() {
|
|
2578
|
+
return true;
|
|
2579
|
+
}
|
|
2580
|
+
get osuScoreMultiplier() {
|
|
2581
|
+
return 0;
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
/**
|
|
2586
|
+
* Represents the Auto mod.
|
|
2587
|
+
*/
|
|
2588
|
+
class ModAuto extends Mod {
|
|
2589
|
+
constructor() {
|
|
2590
|
+
super();
|
|
2591
|
+
this.acronym = "AT";
|
|
2592
|
+
this.name = "Autoplay";
|
|
2593
|
+
this.droidRanked = false;
|
|
2594
|
+
this.osuRanked = false;
|
|
2595
|
+
this.bitwise = 1 << 11;
|
|
2596
|
+
this.incompatibleMods.add(ModAutopilot).add(ModRelax);
|
|
2597
|
+
}
|
|
2598
|
+
get isDroidRelevant() {
|
|
2599
|
+
return true;
|
|
2600
|
+
}
|
|
2601
|
+
calculateDroidScoreMultiplier() {
|
|
2602
|
+
return 1;
|
|
2603
|
+
}
|
|
2604
|
+
get isOsuRelevant() {
|
|
2605
|
+
return true;
|
|
2606
|
+
}
|
|
2607
|
+
get osuScoreMultiplier() {
|
|
2608
|
+
return 1;
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
/**
|
|
2613
|
+
* Represents a `Mod` that adjusts the playback rate of a track.
|
|
2614
|
+
*/
|
|
2615
|
+
class ModRateAdjust extends Mod {
|
|
2616
|
+
/**
|
|
2617
|
+
* The generic osu!droid score multiplier of this `Mod`.
|
|
2618
|
+
*/
|
|
2619
|
+
get droidScoreMultiplier() {
|
|
2620
|
+
return this.trackRateMultiplier.value >= 1
|
|
2621
|
+
? 1 + (this.trackRateMultiplier.value - 1) * 0.24
|
|
2622
|
+
: Math.pow(0.3, (1 - this.trackRateMultiplier.value) * 4);
|
|
2623
|
+
}
|
|
2624
|
+
/**
|
|
2625
|
+
* Generic getter to determine if this `ModRateAdjust` is relevant.
|
|
2626
|
+
*/
|
|
2627
|
+
get isRelevant() {
|
|
2628
|
+
return this.trackRateMultiplier.value !== 1;
|
|
2629
|
+
}
|
|
2630
|
+
constructor(trackRateMultiplier = 1) {
|
|
2631
|
+
super();
|
|
2632
|
+
this.trackRateMultiplier = new DecimalModSetting("Track rate multiplier", "The multiplier for the track's playback rate after applying this mod.", trackRateMultiplier, 0.5, 2, 0.05, 2);
|
|
2633
|
+
}
|
|
2634
|
+
applyToRate(_, rate) {
|
|
2635
|
+
return rate * this.trackRateMultiplier.value;
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
/**
|
|
2640
|
+
* Represents the Custom Speed mod.
|
|
2641
|
+
*
|
|
2642
|
+
* This is a replacement `Mod` for speed modify in osu!droid and custom rates in osu!lazer.
|
|
2643
|
+
*/
|
|
2644
|
+
class ModCustomSpeed extends ModRateAdjust {
|
|
2645
|
+
constructor() {
|
|
2646
|
+
super(...arguments);
|
|
2647
|
+
this.acronym = "CS";
|
|
2648
|
+
this.name = "Custom Speed";
|
|
2649
|
+
this.droidRanked = true;
|
|
2650
|
+
this.osuRanked = false;
|
|
2651
|
+
}
|
|
2652
|
+
copySettings(mod) {
|
|
2653
|
+
var _a, _b;
|
|
2654
|
+
super.copySettings(mod);
|
|
2655
|
+
this.trackRateMultiplier.value =
|
|
2656
|
+
(_b = (_a = mod.settings) === null || _a === void 0 ? void 0 : _a.rateMultiplier) !== null && _b !== void 0 ? _b : this.trackRateMultiplier.value;
|
|
2657
|
+
}
|
|
2658
|
+
get isDroidRelevant() {
|
|
2659
|
+
return this.isRelevant;
|
|
2660
|
+
}
|
|
2661
|
+
calculateDroidScoreMultiplier() {
|
|
2662
|
+
return this.droidScoreMultiplier;
|
|
2663
|
+
}
|
|
2664
|
+
get isOsuRelevant() {
|
|
2665
|
+
return this.isRelevant;
|
|
2666
|
+
}
|
|
2667
|
+
get osuScoreMultiplier() {
|
|
2668
|
+
// Round to the nearest multiple of 0.1.
|
|
2669
|
+
let value = Math.trunc(this.trackRateMultiplier.value * 10) / 10;
|
|
2670
|
+
// Offset back to 0.
|
|
2671
|
+
--value;
|
|
2672
|
+
return this.trackRateMultiplier.value >= 1
|
|
2673
|
+
? 1 + value / 5
|
|
2674
|
+
: 0.6 + value;
|
|
2675
|
+
}
|
|
2676
|
+
serializeSettings() {
|
|
2677
|
+
return { rateMultiplier: this.trackRateMultiplier.value };
|
|
2678
|
+
}
|
|
2679
|
+
toString() {
|
|
2680
|
+
return `${super.toString()} (${this.trackRateMultiplier.toDisplayString()}x)`;
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2684
|
+
/**
|
|
2685
|
+
* Represents a `Mod` that adjusts the size of `HitObject`s during their fade in animation.
|
|
2686
|
+
*/
|
|
2687
|
+
class ModObjectScaleTween extends Mod {
|
|
2688
|
+
constructor() {
|
|
2689
|
+
super();
|
|
2690
|
+
/**
|
|
2691
|
+
* The final size multiplier applied to all [HitObject]s.
|
|
2692
|
+
*/
|
|
2693
|
+
this.endScale = 1;
|
|
2694
|
+
this.incompatibleMods.add(ModObjectScaleTween).add(ModTraceable);
|
|
2695
|
+
}
|
|
2696
|
+
copySettings(mod) {
|
|
2697
|
+
var _a;
|
|
2698
|
+
super.copySettings(mod);
|
|
2699
|
+
const { settings } = mod;
|
|
2700
|
+
this.startScale.value =
|
|
2701
|
+
(_a = settings === null || settings === void 0 ? void 0 : settings.startScale) !== null && _a !== void 0 ? _a : this.startScale.value;
|
|
2702
|
+
}
|
|
2703
|
+
serializeSettings() {
|
|
2704
|
+
return { startScale: this.startScale.value };
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2708
|
+
/**
|
|
2709
|
+
* Represents the Deflate mod.
|
|
2710
|
+
*/
|
|
2711
|
+
class ModDeflate extends ModObjectScaleTween {
|
|
2712
|
+
constructor() {
|
|
2713
|
+
super(...arguments);
|
|
2714
|
+
this.name = "Deflate";
|
|
2715
|
+
this.acronym = "DF";
|
|
2716
|
+
this.startScale = new DecimalModSetting("Start scale", "The initial size multiplier applied to all hit objects.", 2, 1, 25, 0.1, 1);
|
|
2717
|
+
}
|
|
2718
|
+
get droidRanked() {
|
|
2719
|
+
return false;
|
|
2720
|
+
}
|
|
2721
|
+
get isDroidRelevant() {
|
|
2722
|
+
return true;
|
|
2723
|
+
}
|
|
2724
|
+
calculateDroidScoreMultiplier() {
|
|
2725
|
+
return 1;
|
|
2726
|
+
}
|
|
2727
|
+
get osuRanked() {
|
|
2728
|
+
return false;
|
|
2729
|
+
}
|
|
2730
|
+
get isOsuRelevant() {
|
|
2731
|
+
return true;
|
|
2732
|
+
}
|
|
2733
|
+
get osuScoreMultiplier() {
|
|
2734
|
+
return 1;
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2738
|
+
/**
|
|
2739
|
+
* Represents a circle in a beatmap.
|
|
2740
|
+
*
|
|
2741
|
+
* All we need from circles is their position. All positions
|
|
2742
|
+
* stored in the objects are in playfield coordinates (512*384
|
|
2743
|
+
* rectangle).
|
|
2744
|
+
*/
|
|
2745
|
+
class Circle extends HitObject {
|
|
2746
|
+
constructor(values) {
|
|
2747
|
+
super(values);
|
|
2748
|
+
}
|
|
2749
|
+
toString() {
|
|
2750
|
+
return `Position: [${this._position.x}, ${this._position.y}]`;
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2754
|
+
/**
|
|
2755
|
+
* Represents the Replay V6 mod.
|
|
2756
|
+
*
|
|
2757
|
+
* Some behavior of beatmap parsing was changed in replay version 7. More specifically, object stacking
|
|
2758
|
+
* behavior now matches osu!stable and osu!lazer.
|
|
2759
|
+
*
|
|
2760
|
+
* This `Mod` is meant to reapply the stacking behavior prior to replay version 7 to a `Beatmap` that
|
|
2761
|
+
* was played in replays recorded in version 6 and older for replayability and difficulty calculation.
|
|
2762
|
+
*/
|
|
2763
|
+
class ModReplayV6 extends Mod {
|
|
2764
|
+
constructor() {
|
|
2765
|
+
super(...arguments);
|
|
2766
|
+
this.name = "Replay V6";
|
|
2767
|
+
this.acronym = "RV6";
|
|
2768
|
+
this.userPlayable = false;
|
|
2769
|
+
this.droidRanked = false;
|
|
2770
|
+
this.facilitateAdjustment = true;
|
|
2771
|
+
}
|
|
2772
|
+
get isDroidRelevant() {
|
|
2773
|
+
return true;
|
|
2774
|
+
}
|
|
2775
|
+
calculateDroidScoreMultiplier() {
|
|
2776
|
+
return 1;
|
|
2777
|
+
}
|
|
2778
|
+
applyToBeatmap(beatmap) {
|
|
2779
|
+
const { objects } = beatmap.hitObjects;
|
|
2780
|
+
if (objects.length === 0) {
|
|
2781
|
+
return;
|
|
2782
|
+
}
|
|
2783
|
+
// Reset stacking
|
|
2784
|
+
objects.forEach((h) => {
|
|
2785
|
+
h.stackHeight = 0;
|
|
2786
|
+
});
|
|
2787
|
+
for (let i = 0; i < objects.length - 1; ++i) {
|
|
2788
|
+
const current = objects[i];
|
|
2789
|
+
const next = objects[i + 1];
|
|
2790
|
+
this.revertObjectScale(current, beatmap.difficulty);
|
|
2791
|
+
this.revertObjectScale(next, beatmap.difficulty);
|
|
2792
|
+
const convertedScale = CircleSizeCalculator.standardScaleToOldDroidScale(objects[0].scale);
|
|
2793
|
+
if (current instanceof Circle &&
|
|
2794
|
+
next.startTime - current.startTime <
|
|
2795
|
+
2000 * beatmap.general.stackLeniency &&
|
|
2796
|
+
next.position.getDistance(current.position) <
|
|
2797
|
+
Math.sqrt(convertedScale)) {
|
|
2798
|
+
next.stackHeight = current.stackHeight + 1;
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2301
2801
|
}
|
|
2302
|
-
|
|
2303
|
-
|
|
2802
|
+
revertObjectScale(hitObject, difficulty) {
|
|
2803
|
+
const droidScale = CircleSizeCalculator.droidCSToOldDroidScale(difficulty.cs);
|
|
2804
|
+
const radius = CircleSizeCalculator.oldDroidScaleToStandardRadius(droidScale);
|
|
2805
|
+
const standardCS = CircleSizeCalculator.standardRadiusToStandardCS(radius, true);
|
|
2806
|
+
hitObject.scale = CircleSizeCalculator.standardCSToStandardScale(standardCS, true);
|
|
2807
|
+
hitObject.stackOffsetMultiplier = 4;
|
|
2304
2808
|
}
|
|
2305
2809
|
}
|
|
2306
|
-
Slider.baseNormalSlideSample = new BankHitSampleInfo("sliderslide");
|
|
2307
|
-
Slider.baseWhistleSlideSample = new BankHitSampleInfo("sliderwhistle");
|
|
2308
|
-
Slider.baseTickSample = new BankHitSampleInfo("slidertick");
|
|
2309
|
-
Slider.legacyLastTickOffset = 36;
|
|
2310
2810
|
|
|
2311
2811
|
/**
|
|
2312
2812
|
* Represents a `Mod` specific setting that is constrained to a number of values.
|
|
@@ -2314,6 +2814,23 @@ Slider.legacyLastTickOffset = 36;
|
|
|
2314
2814
|
* The value can be `null`, which is treated as a special case.
|
|
2315
2815
|
*/
|
|
2316
2816
|
class NullableDecimalModSetting extends RangeConstrainedModSetting {
|
|
2817
|
+
/**
|
|
2818
|
+
* The number of decimal places to round the value to.
|
|
2819
|
+
*
|
|
2820
|
+
* When set to `null`, the value will not be rounded.
|
|
2821
|
+
*/
|
|
2822
|
+
get precision() {
|
|
2823
|
+
return this._precision;
|
|
2824
|
+
}
|
|
2825
|
+
set precision(value) {
|
|
2826
|
+
if (value !== null && value < 0) {
|
|
2827
|
+
throw new RangeError(`The precision (${value}) must be greater than or equal to 0.`);
|
|
2828
|
+
}
|
|
2829
|
+
this._precision = value;
|
|
2830
|
+
if (value !== null) {
|
|
2831
|
+
this.value = this.processValue(this.value);
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2317
2834
|
constructor(name, description, defaultValue, min = -Number.MAX_VALUE, max = Number.MAX_VALUE, step = 0, precision = null) {
|
|
2318
2835
|
super(name, description, defaultValue, min, max, step);
|
|
2319
2836
|
this.displayFormatter = (v) => {
|
|
@@ -2335,7 +2852,7 @@ class NullableDecimalModSetting extends RangeConstrainedModSetting {
|
|
|
2335
2852
|
(defaultValue < min || defaultValue > max)) {
|
|
2336
2853
|
throw new RangeError(`The default value (${defaultValue}) must be between the minimum (${min}) and maximum (${max}) values.`);
|
|
2337
2854
|
}
|
|
2338
|
-
this.
|
|
2855
|
+
this._precision = precision;
|
|
2339
2856
|
}
|
|
2340
2857
|
processValue(value) {
|
|
2341
2858
|
if (value === null) {
|
|
@@ -2426,18 +2943,19 @@ class ModDifficultyAdjust extends Mod {
|
|
|
2426
2943
|
difficulty.ar = (_b = this.ar.value) !== null && _b !== void 0 ? _b : difficulty.ar;
|
|
2427
2944
|
difficulty.od = (_c = this.od.value) !== null && _c !== void 0 ? _c : difficulty.od;
|
|
2428
2945
|
difficulty.hp = (_d = this.hp.value) !== null && _d !== void 0 ? _d : difficulty.hp;
|
|
2429
|
-
// Special case for force AR, where the AR value is kept constant with
|
|
2430
|
-
// This makes the player perceive the AR as is under all speed multipliers.
|
|
2431
|
-
if (this.ar.value !== null) {
|
|
2946
|
+
// Special case for force AR in replay version 6 and older, where the AR value is kept constant with
|
|
2947
|
+
// respect to game time. This makes the player perceive the AR as is under all speed multipliers.
|
|
2948
|
+
if (this.ar.value !== null && mods.has(ModReplayV6)) {
|
|
2432
2949
|
const preempt = BeatmapDifficulty.difficultyRange(this.ar.value, HitObject.preemptMax, HitObject.preemptMid, HitObject.preemptMin);
|
|
2433
2950
|
const trackRate = this.calculateTrackRate(mods.values());
|
|
2434
2951
|
difficulty.ar = BeatmapDifficulty.inverseDifficultyRange(preempt * trackRate, HitObject.preemptMax, HitObject.preemptMid, HitObject.preemptMin);
|
|
2435
2952
|
}
|
|
2436
2953
|
}
|
|
2437
2954
|
applyToHitObjectWithMods(_, hitObject, mods) {
|
|
2438
|
-
// Special case for force AR, where the AR value is kept constant with
|
|
2439
|
-
// This makes the player perceive the fade in animation as is under all speed
|
|
2440
|
-
|
|
2955
|
+
// Special case for force AR in replay version 6 and older, where the AR value is kept constant with
|
|
2956
|
+
// respect to game time. This makes the player perceive the fade in animation as is under all speed
|
|
2957
|
+
// multipliers.
|
|
2958
|
+
if (this.ar.value === null || !mods.has(ModReplayV6)) {
|
|
2441
2959
|
return;
|
|
2442
2960
|
}
|
|
2443
2961
|
this.applyFadeAdjustment(hitObject, mods);
|
|
@@ -2485,14 +3003,6 @@ class ModDifficultyAdjust extends Mod {
|
|
|
2485
3003
|
}
|
|
2486
3004
|
return rate;
|
|
2487
3005
|
}
|
|
2488
|
-
equals(other) {
|
|
2489
|
-
return (super.equals(other) &&
|
|
2490
|
-
other instanceof ModDifficultyAdjust &&
|
|
2491
|
-
this.cs.value === other.cs.value &&
|
|
2492
|
-
this.ar.value === other.ar.value &&
|
|
2493
|
-
this.od.value === other.od.value &&
|
|
2494
|
-
this.hp.value === other.hp.value);
|
|
2495
|
-
}
|
|
2496
3006
|
toString() {
|
|
2497
3007
|
if (!this.isRelevant) {
|
|
2498
3008
|
return super.toString();
|
|
@@ -2595,22 +3105,6 @@ class ModDoubleTime extends ModRateAdjust {
|
|
|
2595
3105
|
}
|
|
2596
3106
|
}
|
|
2597
3107
|
|
|
2598
|
-
/**
|
|
2599
|
-
* Represents a circle in a beatmap.
|
|
2600
|
-
*
|
|
2601
|
-
* All we need from circles is their position. All positions
|
|
2602
|
-
* stored in the objects are in playfield coordinates (512*384
|
|
2603
|
-
* rectangle).
|
|
2604
|
-
*/
|
|
2605
|
-
class Circle extends HitObject {
|
|
2606
|
-
constructor(values) {
|
|
2607
|
-
super(values);
|
|
2608
|
-
}
|
|
2609
|
-
toString() {
|
|
2610
|
-
return `Position: [${this._position.x}, ${this._position.y}]`;
|
|
2611
|
-
}
|
|
2612
|
-
}
|
|
2613
|
-
|
|
2614
3108
|
var _a$1;
|
|
2615
3109
|
/**
|
|
2616
3110
|
* Represents the osu! playfield.
|
|
@@ -2648,10 +3142,10 @@ class Spinner extends HitObject {
|
|
|
2648
3142
|
this.auxiliarySamples.push(new SequenceHitSampleInfo(samplePoints.map((s) => new TimedHitSampleInfo(s.time, s.applyTo(Spinner.baseSpinnerSpinSample)))));
|
|
2649
3143
|
this.auxiliarySamples.push(new SequenceHitSampleInfo(samplePoints.map((s) => new TimedHitSampleInfo(s.time, s.applyTo(Spinner.baseSpinnerBonusSample)))));
|
|
2650
3144
|
}
|
|
2651
|
-
|
|
3145
|
+
get stackedPosition() {
|
|
2652
3146
|
return this.position;
|
|
2653
3147
|
}
|
|
2654
|
-
|
|
3148
|
+
get stackedEndPosition() {
|
|
2655
3149
|
return this.position;
|
|
2656
3150
|
}
|
|
2657
3151
|
createHitWindow() {
|
|
@@ -3867,11 +4361,6 @@ class ModMirror extends Mod {
|
|
|
3867
4361
|
serializeSettings() {
|
|
3868
4362
|
return { flippedAxes: this.flippedAxes.value - 1 };
|
|
3869
4363
|
}
|
|
3870
|
-
equals(other) {
|
|
3871
|
-
return (super.equals(other) &&
|
|
3872
|
-
other instanceof ModMirror &&
|
|
3873
|
-
other.flippedAxes.value === this.flippedAxes.value);
|
|
3874
|
-
}
|
|
3875
4364
|
toString() {
|
|
3876
4365
|
const settings = [];
|
|
3877
4366
|
if (this.flippedAxes.value === exports.Axes.x ||
|
|
@@ -3886,62 +4375,6 @@ class ModMirror extends Mod {
|
|
|
3886
4375
|
}
|
|
3887
4376
|
}
|
|
3888
4377
|
|
|
3889
|
-
/**
|
|
3890
|
-
* Represents the Replay V6 mod.
|
|
3891
|
-
*
|
|
3892
|
-
* Some behavior of beatmap parsing was changed in replay version 7. More specifically, object stacking
|
|
3893
|
-
* behavior now matches osu!stable and osu!lazer.
|
|
3894
|
-
*
|
|
3895
|
-
* This `Mod` is meant to reapply the stacking behavior prior to replay version 7 to a `Beatmap` that
|
|
3896
|
-
* was played in replays recorded in version 6 and older for replayability and difficulty calculation.
|
|
3897
|
-
*/
|
|
3898
|
-
class ModReplayV6 extends Mod {
|
|
3899
|
-
constructor() {
|
|
3900
|
-
super(...arguments);
|
|
3901
|
-
this.name = "Replay V6";
|
|
3902
|
-
this.acronym = "RV6";
|
|
3903
|
-
this.userPlayable = false;
|
|
3904
|
-
this.droidRanked = false;
|
|
3905
|
-
this.facilitateAdjustment = true;
|
|
3906
|
-
}
|
|
3907
|
-
get isDroidRelevant() {
|
|
3908
|
-
return true;
|
|
3909
|
-
}
|
|
3910
|
-
calculateDroidScoreMultiplier() {
|
|
3911
|
-
return 1;
|
|
3912
|
-
}
|
|
3913
|
-
applyToBeatmap(beatmap) {
|
|
3914
|
-
const { objects } = beatmap.hitObjects;
|
|
3915
|
-
if (objects.length === 0) {
|
|
3916
|
-
return;
|
|
3917
|
-
}
|
|
3918
|
-
// Reset stacking
|
|
3919
|
-
objects.forEach((h) => {
|
|
3920
|
-
h.stackHeight = 0;
|
|
3921
|
-
});
|
|
3922
|
-
for (let i = 0; i < objects.length - 1; ++i) {
|
|
3923
|
-
const current = objects[i];
|
|
3924
|
-
const next = objects[i + 1];
|
|
3925
|
-
this.revertObjectScale(current, beatmap.difficulty);
|
|
3926
|
-
this.revertObjectScale(next, beatmap.difficulty);
|
|
3927
|
-
const convertedScale = CircleSizeCalculator.standardScaleToOldDroidScale(objects[0].scale);
|
|
3928
|
-
if (current instanceof Circle &&
|
|
3929
|
-
next.startTime - current.startTime <
|
|
3930
|
-
2000 * beatmap.general.stackLeniency &&
|
|
3931
|
-
next.position.getDistance(current.position) <
|
|
3932
|
-
Math.sqrt(convertedScale)) {
|
|
3933
|
-
next.stackHeight = current.stackHeight + 1;
|
|
3934
|
-
}
|
|
3935
|
-
}
|
|
3936
|
-
}
|
|
3937
|
-
revertObjectScale(hitObject, difficulty) {
|
|
3938
|
-
const droidScale = CircleSizeCalculator.droidCSToOldDroidScale(difficulty.cs);
|
|
3939
|
-
const radius = CircleSizeCalculator.oldDroidScaleToStandardRadius(droidScale);
|
|
3940
|
-
const standardCS = CircleSizeCalculator.standardRadiusToStandardCS(radius, true);
|
|
3941
|
-
hitObject.scale = CircleSizeCalculator.standardCSToStandardScale(standardCS, true);
|
|
3942
|
-
}
|
|
3943
|
-
}
|
|
3944
|
-
|
|
3945
4378
|
/**
|
|
3946
4379
|
* Represents the HardRock mod.
|
|
3947
4380
|
*/
|
|
@@ -4068,11 +4501,6 @@ class ModFlashlight extends Mod {
|
|
|
4068
4501
|
serializeSettings() {
|
|
4069
4502
|
return { areaFollowDelay: this.followDelay.value };
|
|
4070
4503
|
}
|
|
4071
|
-
equals(other) {
|
|
4072
|
-
return (super.equals(other) &&
|
|
4073
|
-
other instanceof ModFlashlight &&
|
|
4074
|
-
other.followDelay.value === this.followDelay.value);
|
|
4075
|
-
}
|
|
4076
4504
|
toString() {
|
|
4077
4505
|
if (this.followDelay.value === ModFlashlight.defaultFollowDelay) {
|
|
4078
4506
|
return super.toString();
|
|
@@ -4086,22 +4514,26 @@ class ModFlashlight extends Mod {
|
|
|
4086
4514
|
ModFlashlight.defaultFollowDelay = 0.12;
|
|
4087
4515
|
|
|
4088
4516
|
/**
|
|
4089
|
-
* Represents the
|
|
4517
|
+
* Represents the Grow mod.
|
|
4090
4518
|
*/
|
|
4091
|
-
class
|
|
4519
|
+
class ModGrow extends ModObjectScaleTween {
|
|
4092
4520
|
constructor() {
|
|
4093
|
-
super();
|
|
4094
|
-
this.
|
|
4095
|
-
this.
|
|
4096
|
-
this.
|
|
4097
|
-
|
|
4098
|
-
|
|
4521
|
+
super(...arguments);
|
|
4522
|
+
this.name = "Grow";
|
|
4523
|
+
this.acronym = "GR";
|
|
4524
|
+
this.startScale = new DecimalModSetting("Start scale", "The initial size multiplier applied to all hit objects.", 0.5, 0, 0.99, 0.01, 2);
|
|
4525
|
+
}
|
|
4526
|
+
get droidRanked() {
|
|
4527
|
+
return false;
|
|
4099
4528
|
}
|
|
4100
4529
|
get isDroidRelevant() {
|
|
4101
4530
|
return true;
|
|
4102
4531
|
}
|
|
4103
4532
|
calculateDroidScoreMultiplier() {
|
|
4104
|
-
return 1
|
|
4533
|
+
return 1;
|
|
4534
|
+
}
|
|
4535
|
+
get osuRanked() {
|
|
4536
|
+
return false;
|
|
4105
4537
|
}
|
|
4106
4538
|
get isOsuRelevant() {
|
|
4107
4539
|
return true;
|
|
@@ -4112,82 +4544,92 @@ class ModTraceable extends Mod {
|
|
|
4112
4544
|
}
|
|
4113
4545
|
|
|
4114
4546
|
/**
|
|
4115
|
-
* Represents a `Mod` specific setting that is constrained to a
|
|
4547
|
+
* Represents a `Mod` specific setting that is constrained to a range of integer values.
|
|
4116
4548
|
*/
|
|
4117
|
-
class
|
|
4118
|
-
constructor() {
|
|
4119
|
-
super(
|
|
4120
|
-
this.displayFormatter = (v) => v ? "Enabled" : "Disabled";
|
|
4549
|
+
class IntegerModSetting extends NumberModSetting {
|
|
4550
|
+
constructor(name, description, defaultValue, min = -2147483648, max = 2147483647) {
|
|
4551
|
+
super(name, description, defaultValue, min, max, 1);
|
|
4121
4552
|
}
|
|
4122
4553
|
}
|
|
4123
4554
|
|
|
4124
4555
|
/**
|
|
4125
|
-
* Represents the
|
|
4556
|
+
* Represents the Muted mod.
|
|
4126
4557
|
*/
|
|
4127
|
-
class
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
this.acronym = "HD";
|
|
4131
|
-
this.name = "Hidden";
|
|
4132
|
-
this.droidRanked = true;
|
|
4133
|
-
this.osuRanked = true;
|
|
4134
|
-
this.bitwise = 1 << 3;
|
|
4135
|
-
/**
|
|
4136
|
-
* Whether to only fade approach circles.
|
|
4137
|
-
*
|
|
4138
|
-
* The main object body will not fade when enabled.
|
|
4139
|
-
*/
|
|
4140
|
-
this.onlyFadeApproachCircles = new BooleanModSetting("Only fade approach circles", "The main object body will not fade when enabled.", false);
|
|
4141
|
-
this.incompatibleMods.add(ModTraceable);
|
|
4558
|
+
class ModMuted extends Mod {
|
|
4559
|
+
get droidRanked() {
|
|
4560
|
+
return false;
|
|
4142
4561
|
}
|
|
4143
4562
|
get isDroidRelevant() {
|
|
4144
4563
|
return true;
|
|
4145
4564
|
}
|
|
4146
4565
|
calculateDroidScoreMultiplier() {
|
|
4147
|
-
return 1
|
|
4566
|
+
return 1;
|
|
4567
|
+
}
|
|
4568
|
+
get osuRanked() {
|
|
4569
|
+
return true;
|
|
4148
4570
|
}
|
|
4149
4571
|
get isOsuRelevant() {
|
|
4150
4572
|
return true;
|
|
4151
4573
|
}
|
|
4152
4574
|
get osuScoreMultiplier() {
|
|
4153
|
-
return 1
|
|
4575
|
+
return 1;
|
|
4576
|
+
}
|
|
4577
|
+
constructor() {
|
|
4578
|
+
super();
|
|
4579
|
+
this.name = "Muted";
|
|
4580
|
+
this.acronym = "MU";
|
|
4581
|
+
/**
|
|
4582
|
+
* Increase volume as combo builds.
|
|
4583
|
+
*/
|
|
4584
|
+
this.inverseMuting = new BooleanModSetting("Start muted", "Increase volume as combo builds.", false);
|
|
4585
|
+
/**
|
|
4586
|
+
* Add a metronome beat to help the player keep track of the rhythm.
|
|
4587
|
+
*/
|
|
4588
|
+
this.enableMetronome = new BooleanModSetting("Enable metronome", "Add a metronome beat to help you keep track of the rhythm.", true);
|
|
4589
|
+
/**
|
|
4590
|
+
* The combo count at which point the track reaches its final volume.
|
|
4591
|
+
*/
|
|
4592
|
+
this.muteComboCount = new IntegerModSetting("Final volume at combo", "The combo count at which point the track reaches its final volume.", 100, 0, 500);
|
|
4593
|
+
/**
|
|
4594
|
+
* Hit sounds are also muted alongside the track.
|
|
4595
|
+
*/
|
|
4596
|
+
this.affectsHitSounds = new BooleanModSetting("Mute hit sounds", "Hit sounds are also muted alongside the track.", true);
|
|
4597
|
+
this.inverseMuting.bindValueChanged((_, newValue) => {
|
|
4598
|
+
this.muteComboCount.min = newValue ? 1 : 0;
|
|
4599
|
+
}, true);
|
|
4600
|
+
}
|
|
4601
|
+
/**
|
|
4602
|
+
* Obtains the volume at a given combo.
|
|
4603
|
+
*
|
|
4604
|
+
* @param combo The combo.
|
|
4605
|
+
* @return The volume at `combo`, where 0 is muted and 1 is full volume.
|
|
4606
|
+
*/
|
|
4607
|
+
volumeAt(combo) {
|
|
4608
|
+
const volume = MathUtils.clamp(combo / Math.max(1, this.muteComboCount.value), 0, 1);
|
|
4609
|
+
return this.inverseMuting.value ? volume : 1 - volume;
|
|
4154
4610
|
}
|
|
4155
4611
|
copySettings(mod) {
|
|
4156
|
-
var _a, _b;
|
|
4612
|
+
var _a, _b, _c, _d;
|
|
4157
4613
|
super.copySettings(mod);
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
}
|
|
4168
|
-
};
|
|
4169
|
-
beatmap.hitObjects.objects.forEach(applyFadeInAdjustment);
|
|
4614
|
+
const { settings } = mod;
|
|
4615
|
+
this.inverseMuting.value =
|
|
4616
|
+
(_a = settings === null || settings === void 0 ? void 0 : settings.inverseMuting) !== null && _a !== void 0 ? _a : this.inverseMuting.value;
|
|
4617
|
+
this.enableMetronome.value =
|
|
4618
|
+
(_b = settings === null || settings === void 0 ? void 0 : settings.enableMetronome) !== null && _b !== void 0 ? _b : this.enableMetronome.value;
|
|
4619
|
+
this.muteComboCount.value =
|
|
4620
|
+
(_c = settings === null || settings === void 0 ? void 0 : settings.muteComboCount) !== null && _c !== void 0 ? _c : this.muteComboCount.value;
|
|
4621
|
+
this.affectsHitSounds.value =
|
|
4622
|
+
(_d = settings === null || settings === void 0 ? void 0 : settings.affectsHitSounds) !== null && _d !== void 0 ? _d : this.affectsHitSounds.value;
|
|
4170
4623
|
}
|
|
4171
4624
|
serializeSettings() {
|
|
4172
|
-
return
|
|
4173
|
-
|
|
4174
|
-
:
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
other instanceof ModHidden &&
|
|
4179
|
-
other.onlyFadeApproachCircles.value ===
|
|
4180
|
-
this.onlyFadeApproachCircles.value);
|
|
4181
|
-
}
|
|
4182
|
-
toString() {
|
|
4183
|
-
if (!this.onlyFadeApproachCircles.value) {
|
|
4184
|
-
return super.toString();
|
|
4185
|
-
}
|
|
4186
|
-
return `${super.toString()} (approach circles only)`;
|
|
4625
|
+
return {
|
|
4626
|
+
inverseMuting: this.inverseMuting.value,
|
|
4627
|
+
enableMetronome: this.enableMetronome.value,
|
|
4628
|
+
muteComboCount: this.muteComboCount.value,
|
|
4629
|
+
affectsHitSounds: this.affectsHitSounds.value,
|
|
4630
|
+
};
|
|
4187
4631
|
}
|
|
4188
4632
|
}
|
|
4189
|
-
ModHidden.fadeInDurationMultiplier = 0.4;
|
|
4190
|
-
ModHidden.fadeOutDurationMultiplier = 0.3;
|
|
4191
4633
|
|
|
4192
4634
|
/**
|
|
4193
4635
|
* Represents the SuddenDeath mod.
|
|
@@ -4596,12 +5038,6 @@ class ModRandom extends Mod {
|
|
|
4596
5038
|
1 && positionInfos[i - 1].hitObject.isNewCombo;
|
|
4597
5039
|
return previousObjectStartedCombo && this.random.nextDouble() < 0.6;
|
|
4598
5040
|
}
|
|
4599
|
-
equals(other) {
|
|
4600
|
-
return (super.equals(other) &&
|
|
4601
|
-
other instanceof ModRandom &&
|
|
4602
|
-
other.seed.value === this.seed.value &&
|
|
4603
|
-
other.angleSharpness.value === this.angleSharpness.value);
|
|
4604
|
-
}
|
|
4605
5041
|
toString() {
|
|
4606
5042
|
const settings = [];
|
|
4607
5043
|
if (this.seed.value !== null) {
|
|
@@ -4785,51 +5221,6 @@ class ModTouchDevice extends Mod {
|
|
|
4785
5221
|
}
|
|
4786
5222
|
}
|
|
4787
5223
|
|
|
4788
|
-
/**
|
|
4789
|
-
* Types of easing.
|
|
4790
|
-
*
|
|
4791
|
-
* See {@link http://easings.net/ this} page for more samples.
|
|
4792
|
-
*/
|
|
4793
|
-
exports.Easing = void 0;
|
|
4794
|
-
(function (Easing) {
|
|
4795
|
-
Easing[Easing["none"] = 0] = "none";
|
|
4796
|
-
Easing[Easing["out"] = 1] = "out";
|
|
4797
|
-
Easing[Easing["in"] = 2] = "in";
|
|
4798
|
-
Easing[Easing["inQuad"] = 3] = "inQuad";
|
|
4799
|
-
Easing[Easing["outQuad"] = 4] = "outQuad";
|
|
4800
|
-
Easing[Easing["inOutQuad"] = 5] = "inOutQuad";
|
|
4801
|
-
Easing[Easing["inCubic"] = 6] = "inCubic";
|
|
4802
|
-
Easing[Easing["outCubic"] = 7] = "outCubic";
|
|
4803
|
-
Easing[Easing["inOutCubic"] = 8] = "inOutCubic";
|
|
4804
|
-
Easing[Easing["inQuart"] = 9] = "inQuart";
|
|
4805
|
-
Easing[Easing["outQuart"] = 10] = "outQuart";
|
|
4806
|
-
Easing[Easing["inOutQuart"] = 11] = "inOutQuart";
|
|
4807
|
-
Easing[Easing["inQuint"] = 12] = "inQuint";
|
|
4808
|
-
Easing[Easing["outQuint"] = 13] = "outQuint";
|
|
4809
|
-
Easing[Easing["inOutQuint"] = 14] = "inOutQuint";
|
|
4810
|
-
Easing[Easing["inSine"] = 15] = "inSine";
|
|
4811
|
-
Easing[Easing["outSine"] = 16] = "outSine";
|
|
4812
|
-
Easing[Easing["inOutSine"] = 17] = "inOutSine";
|
|
4813
|
-
Easing[Easing["inExpo"] = 18] = "inExpo";
|
|
4814
|
-
Easing[Easing["outExpo"] = 19] = "outExpo";
|
|
4815
|
-
Easing[Easing["inOutExpo"] = 20] = "inOutExpo";
|
|
4816
|
-
Easing[Easing["inCirc"] = 21] = "inCirc";
|
|
4817
|
-
Easing[Easing["outCirc"] = 22] = "outCirc";
|
|
4818
|
-
Easing[Easing["inOutCirc"] = 23] = "inOutCirc";
|
|
4819
|
-
Easing[Easing["inElastic"] = 24] = "inElastic";
|
|
4820
|
-
Easing[Easing["outElastic"] = 25] = "outElastic";
|
|
4821
|
-
Easing[Easing["outElasticHalf"] = 26] = "outElasticHalf";
|
|
4822
|
-
Easing[Easing["outElasticQuarter"] = 27] = "outElasticQuarter";
|
|
4823
|
-
Easing[Easing["inOutElastic"] = 28] = "inOutElastic";
|
|
4824
|
-
Easing[Easing["inBack"] = 29] = "inBack";
|
|
4825
|
-
Easing[Easing["outBack"] = 30] = "outBack";
|
|
4826
|
-
Easing[Easing["inOutBack"] = 31] = "inOutBack";
|
|
4827
|
-
Easing[Easing["inBounce"] = 32] = "inBounce";
|
|
4828
|
-
Easing[Easing["outBounce"] = 33] = "outBounce";
|
|
4829
|
-
Easing[Easing["inOutBounce"] = 34] = "inOutBounce";
|
|
4830
|
-
Easing[Easing["outPow10"] = 35] = "outPow10";
|
|
4831
|
-
})(exports.Easing || (exports.Easing = {}));
|
|
4832
|
-
|
|
4833
5224
|
/**
|
|
4834
5225
|
* Holds interpolation methods for numbers and vectors.
|
|
4835
5226
|
*/
|
|
@@ -5041,12 +5432,6 @@ class ModTimeRamp extends Mod {
|
|
|
5041
5432
|
finalRate: this.finalRate.value,
|
|
5042
5433
|
};
|
|
5043
5434
|
}
|
|
5044
|
-
equals(other) {
|
|
5045
|
-
return (super.equals(other) &&
|
|
5046
|
-
other instanceof ModTimeRamp &&
|
|
5047
|
-
other.initialRate.value === this.initialRate.value &&
|
|
5048
|
-
other.finalRate.value === this.finalRate.value);
|
|
5049
|
-
}
|
|
5050
5435
|
toString() {
|
|
5051
5436
|
return `${super.toString()} (${this.initialRate.toDisplayString()}x - ${this.finalRate.toDisplayString()}x)`;
|
|
5052
5437
|
}
|
|
@@ -5344,6 +5729,8 @@ ModUtil.allMods = (() => {
|
|
|
5344
5729
|
ModEasy,
|
|
5345
5730
|
ModNoFail,
|
|
5346
5731
|
ModHidden,
|
|
5732
|
+
ModApproachDifferent,
|
|
5733
|
+
ModFreezeFrame,
|
|
5347
5734
|
ModTraceable,
|
|
5348
5735
|
ModDoubleTime,
|
|
5349
5736
|
ModNightCore,
|
|
@@ -5353,6 +5740,8 @@ ModUtil.allMods = (() => {
|
|
|
5353
5740
|
ModWindUp,
|
|
5354
5741
|
ModHardRock,
|
|
5355
5742
|
ModMirror,
|
|
5743
|
+
ModGrow,
|
|
5744
|
+
ModDeflate,
|
|
5356
5745
|
ModDifficultyAdjust,
|
|
5357
5746
|
ModFlashlight,
|
|
5358
5747
|
ModSuddenDeath,
|
|
@@ -5360,6 +5749,7 @@ ModUtil.allMods = (() => {
|
|
|
5360
5749
|
ModPrecise,
|
|
5361
5750
|
ModRandom,
|
|
5362
5751
|
ModReallyEasy,
|
|
5752
|
+
ModMuted,
|
|
5363
5753
|
ModSynesthesia,
|
|
5364
5754
|
ModReplayV6,
|
|
5365
5755
|
ModScoreV2,
|
|
@@ -5387,15 +5777,17 @@ class ModMap extends Map {
|
|
|
5387
5777
|
return this.size === 0;
|
|
5388
5778
|
}
|
|
5389
5779
|
constructor(iterable) {
|
|
5780
|
+
// We are not passing `iterable` here to preserve mod-specific settings.
|
|
5781
|
+
super();
|
|
5390
5782
|
if (Array.isArray(iterable)) {
|
|
5391
5783
|
for (const [key, value] of iterable) {
|
|
5392
5784
|
// Ensure the mod type corresponds to the mod instance.
|
|
5393
5785
|
if (key !== value.constructor) {
|
|
5394
5786
|
throw new TypeError(`Key ${key.name} does not match value ${value.constructor.name}`);
|
|
5395
5787
|
}
|
|
5788
|
+
this.set(value);
|
|
5396
5789
|
}
|
|
5397
5790
|
}
|
|
5398
|
-
super(iterable);
|
|
5399
5791
|
}
|
|
5400
5792
|
has(keyOrValue) {
|
|
5401
5793
|
const key = keyOrValue instanceof Mod
|
|
@@ -5586,6 +5978,9 @@ class BeatmapHitObjects {
|
|
|
5586
5978
|
this._sliders = 0;
|
|
5587
5979
|
this._spinners = 0;
|
|
5588
5980
|
}
|
|
5981
|
+
[Symbol.iterator]() {
|
|
5982
|
+
return this.objects[Symbol.iterator]();
|
|
5983
|
+
}
|
|
5589
5984
|
/**
|
|
5590
5985
|
* Finds the insertion index of a hitobject in a given time.
|
|
5591
5986
|
*
|
|
@@ -5936,7 +6331,7 @@ class PlayableBeatmap {
|
|
|
5936
6331
|
this.hitObjects = baseBeatmap.hitObjects;
|
|
5937
6332
|
this.maxCombo = baseBeatmap.maxCombo;
|
|
5938
6333
|
this.mods = mods;
|
|
5939
|
-
this.speedMultiplier = ModUtil.calculateRateWithMods(this.mods.values());
|
|
6334
|
+
this.speedMultiplier = ModUtil.calculateRateWithMods(this.mods.values(), Number.POSITIVE_INFINITY);
|
|
5940
6335
|
}
|
|
5941
6336
|
}
|
|
5942
6337
|
|
|
@@ -6151,6 +6546,9 @@ class ControlPointManager {
|
|
|
6151
6546
|
// l will be the first control point with time > this._points[l].time, but we want the one before it
|
|
6152
6547
|
return this._points[l - 1];
|
|
6153
6548
|
}
|
|
6549
|
+
[Symbol.iterator]() {
|
|
6550
|
+
return this._points[Symbol.iterator]();
|
|
6551
|
+
}
|
|
6154
6552
|
/**
|
|
6155
6553
|
* Finds the insertion index of a control point in a given time.
|
|
6156
6554
|
*
|
|
@@ -8844,8 +9242,8 @@ class BeatmapDecoder extends Decoder {
|
|
|
8844
9242
|
* @param parseStoryboard Whether to parse the beatmap's storyboard.
|
|
8845
9243
|
*/
|
|
8846
9244
|
decode(str, mode = exports.Modes.osu, parseStoryboard = true) {
|
|
8847
|
-
this.finalResult.mode = mode;
|
|
8848
9245
|
super.decode(str);
|
|
9246
|
+
this.finalResult.mode = mode;
|
|
8849
9247
|
if (parseStoryboard) {
|
|
8850
9248
|
const eventsDecoder = (this.decoders[BeatmapSection.events]);
|
|
8851
9249
|
if (eventsDecoder.storyboardLines.length > 0) {
|
|
@@ -10949,15 +11347,6 @@ ErrorFunction.ervInvImpGd = [
|
|
|
10949
11347
|
0.3999688121938621e-6, 0.1618092908879045e-8, 0.2315586083102596e-11,
|
|
10950
11348
|
];
|
|
10951
11349
|
|
|
10952
|
-
/**
|
|
10953
|
-
* Represents a `Mod` specific setting that is constrained to a range of integer values.
|
|
10954
|
-
*/
|
|
10955
|
-
class IntegerModSetting extends NumberModSetting {
|
|
10956
|
-
constructor(name, description, defaultValue, min = -2147483648, max = 2147483647) {
|
|
10957
|
-
super(name, description, defaultValue, min, max, 1);
|
|
10958
|
-
}
|
|
10959
|
-
}
|
|
10960
|
-
|
|
10961
11350
|
/**
|
|
10962
11351
|
* Ranking status of a beatmap.
|
|
10963
11352
|
*/
|
|
@@ -11475,20 +11864,26 @@ exports.Interpolation = Interpolation;
|
|
|
11475
11864
|
exports.MapInfo = MapInfo;
|
|
11476
11865
|
exports.MathUtils = MathUtils;
|
|
11477
11866
|
exports.Mod = Mod;
|
|
11867
|
+
exports.ModApproachDifferent = ModApproachDifferent;
|
|
11478
11868
|
exports.ModAuto = ModAuto;
|
|
11479
11869
|
exports.ModAutopilot = ModAutopilot;
|
|
11480
11870
|
exports.ModCustomSpeed = ModCustomSpeed;
|
|
11871
|
+
exports.ModDeflate = ModDeflate;
|
|
11481
11872
|
exports.ModDifficultyAdjust = ModDifficultyAdjust;
|
|
11482
11873
|
exports.ModDoubleTime = ModDoubleTime;
|
|
11483
11874
|
exports.ModEasy = ModEasy;
|
|
11484
11875
|
exports.ModFlashlight = ModFlashlight;
|
|
11876
|
+
exports.ModFreezeFrame = ModFreezeFrame;
|
|
11877
|
+
exports.ModGrow = ModGrow;
|
|
11485
11878
|
exports.ModHalfTime = ModHalfTime;
|
|
11486
11879
|
exports.ModHardRock = ModHardRock;
|
|
11487
11880
|
exports.ModHidden = ModHidden;
|
|
11488
11881
|
exports.ModMap = ModMap;
|
|
11489
11882
|
exports.ModMirror = ModMirror;
|
|
11883
|
+
exports.ModMuted = ModMuted;
|
|
11490
11884
|
exports.ModNightCore = ModNightCore;
|
|
11491
11885
|
exports.ModNoFail = ModNoFail;
|
|
11886
|
+
exports.ModObjectScaleTween = ModObjectScaleTween;
|
|
11492
11887
|
exports.ModOldNightCore = ModOldNightCore;
|
|
11493
11888
|
exports.ModPerfect = ModPerfect;
|
|
11494
11889
|
exports.ModPrecise = ModPrecise;
|
|
@@ -11510,6 +11905,7 @@ exports.ModWindDown = ModWindDown;
|
|
|
11510
11905
|
exports.ModWindUp = ModWindUp;
|
|
11511
11906
|
exports.NormalDistribution = NormalDistribution;
|
|
11512
11907
|
exports.NullableDecimalModSetting = NullableDecimalModSetting;
|
|
11908
|
+
exports.NullableIntegerModSetting = NullableIntegerModSetting;
|
|
11513
11909
|
exports.NumberModSetting = NumberModSetting;
|
|
11514
11910
|
exports.OsuAPIRequestBuilder = OsuAPIRequestBuilder;
|
|
11515
11911
|
exports.OsuHitWindow = OsuHitWindow;
|