@rian8337/osu-difficulty-calculator 4.0.0-beta.25 → 4.0.0-beta.27
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 +178 -182
- package/package.json +3 -3
- package/typings/index.d.ts +44 -45
package/dist/index.js
CHANGED
|
@@ -32,6 +32,12 @@ AimEvaluator.velocityChangeMultiplier = 0.75;
|
|
|
32
32
|
* The base of a difficulty calculator.
|
|
33
33
|
*/
|
|
34
34
|
class DifficultyCalculator {
|
|
35
|
+
/**
|
|
36
|
+
* The difficulty objects of the beatmap.
|
|
37
|
+
*/
|
|
38
|
+
get objects() {
|
|
39
|
+
return this._objects;
|
|
40
|
+
}
|
|
35
41
|
/**
|
|
36
42
|
* The total star rating of the beatmap.
|
|
37
43
|
*/
|
|
@@ -41,13 +47,13 @@ class DifficultyCalculator {
|
|
|
41
47
|
/**
|
|
42
48
|
* Constructs a new instance of the calculator.
|
|
43
49
|
*
|
|
44
|
-
* @param beatmap The beatmap to calculate.
|
|
50
|
+
* @param beatmap The beatmap to calculate.
|
|
45
51
|
*/
|
|
46
52
|
constructor(beatmap) {
|
|
47
53
|
/**
|
|
48
54
|
* The difficulty objects of the beatmap.
|
|
49
55
|
*/
|
|
50
|
-
this.
|
|
56
|
+
this._objects = [];
|
|
51
57
|
/**
|
|
52
58
|
* The modifications applied.
|
|
53
59
|
*/
|
|
@@ -62,13 +68,6 @@ class DifficultyCalculator {
|
|
|
62
68
|
flashlight: [],
|
|
63
69
|
};
|
|
64
70
|
this.beatmap = beatmap;
|
|
65
|
-
this.difficultyStatistics = {
|
|
66
|
-
circleSize: beatmap.difficulty.cs,
|
|
67
|
-
approachRate: beatmap.difficulty.ar,
|
|
68
|
-
overallDifficulty: beatmap.difficulty.od,
|
|
69
|
-
healthDrain: beatmap.difficulty.hp,
|
|
70
|
-
overallSpeedMultiplier: 1,
|
|
71
|
-
};
|
|
72
71
|
}
|
|
73
72
|
/**
|
|
74
73
|
* Calculates the star rating of the specified beatmap.
|
|
@@ -93,9 +92,9 @@ class DifficultyCalculator {
|
|
|
93
92
|
mods: this.mods,
|
|
94
93
|
customSpeedMultiplier: options === null || options === void 0 ? void 0 : options.customSpeedMultiplier,
|
|
95
94
|
});
|
|
96
|
-
|
|
97
|
-
this.populateDifficultyAttributes();
|
|
98
|
-
this.
|
|
95
|
+
const clockRate = this.calculateClockRate(options);
|
|
96
|
+
this.populateDifficultyAttributes(playableBeatmap, clockRate);
|
|
97
|
+
this._objects = this.generateDifficultyHitObjects(playableBeatmap, clockRate);
|
|
99
98
|
this.calculateAll();
|
|
100
99
|
return this;
|
|
101
100
|
}
|
|
@@ -112,20 +111,40 @@ class DifficultyCalculator {
|
|
|
112
111
|
}
|
|
113
112
|
}
|
|
114
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Obtains the clock rate of the beatmap.
|
|
116
|
+
*
|
|
117
|
+
* @param options The options to obtain the clock rate with.
|
|
118
|
+
* @returns The clock rate of the beatmap.
|
|
119
|
+
*/
|
|
120
|
+
calculateClockRate(options) {
|
|
121
|
+
var _a, _b;
|
|
122
|
+
return (osuBase.ModUtil.calculateRateWithMods((_a = options === null || options === void 0 ? void 0 : options.mods) !== null && _a !== void 0 ? _a : []) *
|
|
123
|
+
((_b = options === null || options === void 0 ? void 0 : options.customSpeedMultiplier) !== null && _b !== void 0 ? _b : 1));
|
|
124
|
+
}
|
|
115
125
|
/**
|
|
116
126
|
* Populates the stored difficulty attributes with necessary data.
|
|
127
|
+
*
|
|
128
|
+
* @param beatmap The beatmap to populate the attributes with.
|
|
129
|
+
* @param clockRate The clock rate of the beatmap.
|
|
117
130
|
*/
|
|
118
|
-
populateDifficultyAttributes() {
|
|
119
|
-
this.attributes.approachRate = this.difficultyStatistics.approachRate;
|
|
131
|
+
populateDifficultyAttributes(beatmap, clockRate) {
|
|
120
132
|
this.attributes.hitCircleCount = this.beatmap.hitObjects.circles;
|
|
121
133
|
this.attributes.maxCombo = this.beatmap.maxCombo;
|
|
122
134
|
this.attributes.mods = this.mods.slice();
|
|
123
|
-
this.attributes.overallDifficulty =
|
|
124
|
-
this.difficultyStatistics.overallDifficulty;
|
|
125
135
|
this.attributes.sliderCount = this.beatmap.hitObjects.sliders;
|
|
126
136
|
this.attributes.spinnerCount = this.beatmap.hitObjects.spinners;
|
|
127
|
-
this.attributes.clockRate =
|
|
128
|
-
|
|
137
|
+
this.attributes.clockRate = clockRate;
|
|
138
|
+
let greatWindow;
|
|
139
|
+
switch (this.mode) {
|
|
140
|
+
case osuBase.Modes.droid:
|
|
141
|
+
greatWindow = new osuBase.DroidHitWindow(beatmap.difficulty.od).hitWindowFor300(this.mods.some((m) => m instanceof osuBase.ModPrecise));
|
|
142
|
+
break;
|
|
143
|
+
case osuBase.Modes.osu:
|
|
144
|
+
greatWindow = new osuBase.OsuHitWindow(beatmap.difficulty.od).hitWindowFor300();
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
this.attributes.overallDifficulty = osuBase.OsuHitWindow.hitWindow300ToOD(greatWindow / clockRate);
|
|
129
148
|
}
|
|
130
149
|
/**
|
|
131
150
|
* Calculates the star rating value of a difficulty.
|
|
@@ -1057,7 +1076,7 @@ class DroidFlashlight extends DroidSkill {
|
|
|
1057
1076
|
this.reducedSectionCount = 0;
|
|
1058
1077
|
this.reducedSectionBaseline = 1;
|
|
1059
1078
|
this.starsPerDouble = 1.06;
|
|
1060
|
-
this.skillMultiplier = 0.
|
|
1079
|
+
this.skillMultiplier = 0.02;
|
|
1061
1080
|
this.currentFlashlightStrain = 0;
|
|
1062
1081
|
this.isHidden = mods.some((m) => m instanceof osuBase.ModHidden);
|
|
1063
1082
|
this.withSliders = withSliders;
|
|
@@ -1086,64 +1105,42 @@ class DroidFlashlight extends DroidSkill {
|
|
|
1086
1105
|
}
|
|
1087
1106
|
}
|
|
1088
1107
|
difficultyValue() {
|
|
1089
|
-
return
|
|
1108
|
+
return (this.strainPeaks.reduce((a, v) => a + v, 0) * this.starsPerDouble);
|
|
1090
1109
|
}
|
|
1091
1110
|
}
|
|
1092
1111
|
|
|
1093
|
-
/**
|
|
1094
|
-
* An evaluator for calculating rhythm skill.
|
|
1095
|
-
*
|
|
1096
|
-
* This class should be considered an "evaluating" class and not persisted.
|
|
1097
|
-
*/
|
|
1098
|
-
class RhythmEvaluator {
|
|
1099
|
-
}
|
|
1100
|
-
RhythmEvaluator.rhythmMultiplier = 0.75;
|
|
1101
|
-
RhythmEvaluator.historyTimeMax = 5000; // 5 seconds of calculateRhythmBonus max.
|
|
1102
|
-
|
|
1103
1112
|
class Island {
|
|
1104
|
-
constructor(
|
|
1105
|
-
this.
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1113
|
+
constructor(delta, deltaDifferenceEpsilon) {
|
|
1114
|
+
this.delta = Number.MAX_SAFE_INTEGER;
|
|
1115
|
+
this.deltaCount = 0;
|
|
1116
|
+
if (deltaDifferenceEpsilon === undefined) {
|
|
1117
|
+
this.deltaDifferenceEpsilon = delta;
|
|
1118
|
+
}
|
|
1119
|
+
else {
|
|
1120
|
+
this.deltaDifferenceEpsilon = deltaDifferenceEpsilon;
|
|
1121
|
+
this.addDelta(delta);
|
|
1109
1122
|
}
|
|
1110
|
-
this.deltaDifferenceEpsilon = epsilon;
|
|
1111
|
-
this.addDelta(firstDelta);
|
|
1112
1123
|
}
|
|
1113
1124
|
addDelta(delta) {
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
this.
|
|
1118
|
-
}
|
|
1119
|
-
get averageDelta() {
|
|
1120
|
-
return this.deltas.length > 0
|
|
1121
|
-
? Math.max(this.deltas.reduce((a, b) => a + b) / this.deltas.length, DifficultyHitObject.minDeltaTime)
|
|
1122
|
-
: 0;
|
|
1125
|
+
if (this.delta === Number.MAX_SAFE_INTEGER) {
|
|
1126
|
+
this.delta = Math.max(Math.trunc(delta), DifficultyHitObject.minDeltaTime);
|
|
1127
|
+
}
|
|
1128
|
+
++this.deltaCount;
|
|
1123
1129
|
}
|
|
1124
1130
|
isSimilarPolarity(other) {
|
|
1125
|
-
//
|
|
1126
|
-
//
|
|
1127
|
-
return
|
|
1128
|
-
this.deltaDifferenceEpsilon &&
|
|
1129
|
-
this.deltas.length % 2 === other.deltas.length % 2);
|
|
1131
|
+
// TODO: consider islands to be of similar polarity only if they're having the same average delta (we don't want to consider 3 singletaps similar to a triple)
|
|
1132
|
+
// naively adding delta check here breaks _a lot_ of maps because of the flawed ratio calculation
|
|
1133
|
+
return this.deltaCount % 2 == other.deltaCount % 2;
|
|
1130
1134
|
}
|
|
1131
1135
|
equals(other) {
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
}
|
|
1135
|
-
for (let i = 0; i < this.deltas.length; ++i) {
|
|
1136
|
-
if (this.deltas[i] !== other.deltas[i]) {
|
|
1137
|
-
return false;
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
return true;
|
|
1136
|
+
return (Math.abs(this.delta - other.delta) < this.deltaDifferenceEpsilon &&
|
|
1137
|
+
this.deltaCount === other.deltaCount);
|
|
1141
1138
|
}
|
|
1142
1139
|
}
|
|
1143
1140
|
/**
|
|
1144
1141
|
* An evaluator for calculating osu!droid Rhythm skill.
|
|
1145
1142
|
*/
|
|
1146
|
-
class DroidRhythmEvaluator
|
|
1143
|
+
class DroidRhythmEvaluator {
|
|
1147
1144
|
/**
|
|
1148
1145
|
* Calculates a rhythm multiplier for the difficulty of the tap associated
|
|
1149
1146
|
* with historic data of the current object.
|
|
@@ -1151,9 +1148,7 @@ class DroidRhythmEvaluator extends RhythmEvaluator {
|
|
|
1151
1148
|
* @param current The current object.
|
|
1152
1149
|
*/
|
|
1153
1150
|
static evaluateDifficultyOf(current) {
|
|
1154
|
-
if (current.object instanceof osuBase.Spinner
|
|
1155
|
-
// Exclude overlapping objects that can be tapped at once.
|
|
1156
|
-
current.isOverlapping(false)) {
|
|
1151
|
+
if (current.object instanceof osuBase.Spinner) {
|
|
1157
1152
|
return 1;
|
|
1158
1153
|
}
|
|
1159
1154
|
const deltaDifferenceEpsilon = current.fullGreatWindow * 0.3;
|
|
@@ -1183,47 +1178,50 @@ class DroidRhythmEvaluator extends RhythmEvaluator {
|
|
|
1183
1178
|
++rhythmStart;
|
|
1184
1179
|
}
|
|
1185
1180
|
for (let i = rhythmStart; i > 0; --i) {
|
|
1186
|
-
// Scale note 0 to 1 from history to now.
|
|
1187
|
-
let currentHistoricalDecay = (this.historyTimeMax -
|
|
1188
|
-
(current.startTime - validPrevious[i - 1].startTime)) /
|
|
1189
|
-
this.historyTimeMax;
|
|
1190
|
-
// Either we're limited by time or limited by object count.
|
|
1191
|
-
currentHistoricalDecay = Math.min(currentHistoricalDecay, (validPrevious.length - i) / validPrevious.length);
|
|
1192
1181
|
const currentObject = validPrevious[i - 1];
|
|
1193
1182
|
const prevObject = validPrevious[i];
|
|
1194
1183
|
const lastObject = validPrevious[i + 1];
|
|
1184
|
+
// Scale note 0 to 1 from history to now.
|
|
1185
|
+
const timeDecay = (this.historyTimeMax -
|
|
1186
|
+
(current.startTime - currentObject.startTime)) /
|
|
1187
|
+
this.historyTimeMax;
|
|
1188
|
+
const noteDecay = (validPrevious.length - i) / validPrevious.length;
|
|
1189
|
+
// Either we're limited by time or limited by object count.
|
|
1190
|
+
const currentHistoricalDecay = Math.min(timeDecay, noteDecay);
|
|
1195
1191
|
const currentDelta = currentObject.strainTime;
|
|
1196
1192
|
const prevDelta = prevObject.strainTime;
|
|
1197
1193
|
const lastDelta = lastObject.strainTime;
|
|
1194
|
+
// Calculate how much current delta difference deserves a rhythm bonus
|
|
1195
|
+
// This function is meant to reduce rhythm bonus for deltas that are multiples of each other (i.e. 100 and 200)
|
|
1196
|
+
const deltaDifferenceRatio = Math.min(prevDelta, currentDelta) /
|
|
1197
|
+
Math.max(prevDelta, currentDelta);
|
|
1198
1198
|
const currentRatio = 1 +
|
|
1199
|
-
|
|
1200
|
-
Math.min(0.5, Math.pow(Math.sin(Math.PI /
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
const
|
|
1204
|
-
|
|
1205
|
-
let effectiveRatio = windowPenalty * currentRatio;
|
|
1199
|
+
this.rhythmRatioMultiplier *
|
|
1200
|
+
Math.min(0.5, Math.pow(Math.sin(Math.PI / deltaDifferenceRatio), 2));
|
|
1201
|
+
// Reduce ratio bonus if delta difference is too big
|
|
1202
|
+
const fraction = Math.max(prevDelta / currentDelta, currentDelta / prevDelta);
|
|
1203
|
+
const fractionMultiplier = osuBase.MathUtils.clamp(2 - fraction / 8, 0, 1);
|
|
1204
|
+
const windowPenalty = Math.min(1, Math.max(0, Math.abs(prevDelta - currentDelta) - deltaDifferenceEpsilon) / deltaDifferenceEpsilon);
|
|
1205
|
+
let effectiveRatio = windowPenalty * currentRatio * fractionMultiplier;
|
|
1206
1206
|
if (firstDeltaSwitch) {
|
|
1207
|
-
if (Math.abs(prevDelta - currentDelta)
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
island.addDelta(currentDelta);
|
|
1211
|
-
}
|
|
1207
|
+
if (Math.abs(prevDelta - currentDelta) < deltaDifferenceEpsilon) {
|
|
1208
|
+
// Island is still progressing, count size.
|
|
1209
|
+
island.addDelta(currentDelta);
|
|
1212
1210
|
}
|
|
1213
1211
|
else {
|
|
1214
1212
|
// BPM change is into slider, this is easy acc window.
|
|
1215
1213
|
if (currentObject.object instanceof osuBase.Slider) {
|
|
1216
1214
|
effectiveRatio /= 8;
|
|
1217
1215
|
}
|
|
1218
|
-
// BPM change was from a slider, this is typically
|
|
1219
|
-
// Unintentional side effect is that bursts with kicksliders at the ends might
|
|
1220
|
-
//
|
|
1216
|
+
// BPM change was from a slider, this is easier typically than circle -> circle.
|
|
1217
|
+
// Unintentional side effect is that bursts with kicksliders at the ends might have lower difficulty
|
|
1218
|
+
// than bursts without sliders.
|
|
1221
1219
|
if (prevObject.object instanceof osuBase.Slider) {
|
|
1222
|
-
effectiveRatio
|
|
1220
|
+
effectiveRatio *= 0.3;
|
|
1223
1221
|
}
|
|
1224
1222
|
// Repeated island polarity (2 -> 4, 3 -> 5).
|
|
1225
1223
|
if (island.isSimilarPolarity(previousIsland)) {
|
|
1226
|
-
effectiveRatio
|
|
1224
|
+
effectiveRatio /= 2;
|
|
1227
1225
|
}
|
|
1228
1226
|
// Previous increase happened a note ago.
|
|
1229
1227
|
// Albeit this is a 1/1 -> 1/2-1/4 type of transition, we don't want to buff this.
|
|
@@ -1231,8 +1229,9 @@ class DroidRhythmEvaluator extends RhythmEvaluator {
|
|
|
1231
1229
|
prevDelta > currentDelta + deltaDifferenceEpsilon) {
|
|
1232
1230
|
effectiveRatio /= 8;
|
|
1233
1231
|
}
|
|
1234
|
-
//
|
|
1235
|
-
|
|
1232
|
+
// Repeated island size (ex: triplet -> triplet).
|
|
1233
|
+
// TODO: remove this nerf since its staying here only for balancing purposes because of the flawed ratio calculation
|
|
1234
|
+
if (previousIsland.deltaCount == island.deltaCount) {
|
|
1236
1235
|
effectiveRatio /= 2;
|
|
1237
1236
|
}
|
|
1238
1237
|
let islandFound = false;
|
|
@@ -1249,9 +1248,7 @@ class DroidRhythmEvaluator extends RhythmEvaluator {
|
|
|
1249
1248
|
}
|
|
1250
1249
|
// Repeated island (ex: triplet -> triplet).
|
|
1251
1250
|
// Graph: https://www.desmos.com/calculator/pj7an56zwf
|
|
1252
|
-
effectiveRatio *= Math.min(
|
|
1253
|
-
(1 +
|
|
1254
|
-
Math.exp(10 - 0.165 * island.averageDelta))));
|
|
1251
|
+
effectiveRatio *= Math.min(3 / islandCount, Math.pow(1 / islandCount, 2.75 / (1 + Math.exp(14 - 0.24 * island.delta))));
|
|
1255
1252
|
break;
|
|
1256
1253
|
}
|
|
1257
1254
|
if (!islandFound) {
|
|
@@ -1269,28 +1266,35 @@ class DroidRhythmEvaluator extends RhythmEvaluator {
|
|
|
1269
1266
|
// If we're speeding up, this stays as is and we keep counting island size.
|
|
1270
1267
|
firstDeltaSwitch = false;
|
|
1271
1268
|
}
|
|
1272
|
-
island = new Island(
|
|
1269
|
+
island = new Island(currentDelta, deltaDifferenceEpsilon);
|
|
1273
1270
|
}
|
|
1274
1271
|
}
|
|
1275
|
-
else if (prevDelta >
|
|
1276
|
-
// We
|
|
1272
|
+
else if (prevDelta > currentDelta + deltaDifferenceEpsilon) {
|
|
1273
|
+
// We are speeding up.
|
|
1277
1274
|
// Begin counting island until we change speed again.
|
|
1278
1275
|
firstDeltaSwitch = true;
|
|
1279
|
-
//
|
|
1276
|
+
// BPM change is into slider, this is easy acc window.
|
|
1277
|
+
if (currentObject.object instanceof osuBase.Slider) {
|
|
1278
|
+
effectiveRatio *= 0.6;
|
|
1279
|
+
}
|
|
1280
|
+
// BPM change was from a slider, this is easier typically than circle -> circle
|
|
1281
|
+
// Unintentional side effect is that bursts with kicksliders at the ends might have lower difficulty
|
|
1282
|
+
// than bursts without sliders
|
|
1280
1283
|
if (prevObject.object instanceof osuBase.Slider) {
|
|
1281
|
-
effectiveRatio *= 0.
|
|
1284
|
+
effectiveRatio *= 0.6;
|
|
1282
1285
|
}
|
|
1283
1286
|
startRatio = effectiveRatio;
|
|
1284
|
-
island = new Island(
|
|
1287
|
+
island = new Island(currentDelta, deltaDifferenceEpsilon);
|
|
1285
1288
|
}
|
|
1286
1289
|
}
|
|
1287
|
-
return Math.sqrt(4 + rhythmComplexitySum * this.
|
|
1290
|
+
return (Math.sqrt(4 + rhythmComplexitySum * this.rhythmOverallMultiplier) /
|
|
1291
|
+
2);
|
|
1288
1292
|
}
|
|
1289
1293
|
}
|
|
1290
|
-
DroidRhythmEvaluator.
|
|
1291
|
-
DroidRhythmEvaluator.
|
|
1292
|
-
DroidRhythmEvaluator.
|
|
1293
|
-
DroidRhythmEvaluator.
|
|
1294
|
+
DroidRhythmEvaluator.historyTimeMax = 5000; // 5 seconds of calculateRhythmBonus max.
|
|
1295
|
+
DroidRhythmEvaluator.historyObjectsMax = 32;
|
|
1296
|
+
DroidRhythmEvaluator.rhythmOverallMultiplier = 0.95;
|
|
1297
|
+
DroidRhythmEvaluator.rhythmRatioMultiplier = 12;
|
|
1294
1298
|
|
|
1295
1299
|
/**
|
|
1296
1300
|
* Represents the skill required to properly follow a beatmap's rhythm.
|
|
@@ -1492,9 +1496,8 @@ class DroidDifficultyHitObject extends DifficultyHitObject {
|
|
|
1492
1496
|
* @param difficultyHitObjects All difficulty hitobjects in the processed beatmap.
|
|
1493
1497
|
* @param clockRate The clock rate of the beatmap.
|
|
1494
1498
|
* @param greatWindow The great window of the hitobject.
|
|
1495
|
-
* @param isForceAR Whether force AR is enabled.
|
|
1496
1499
|
*/
|
|
1497
|
-
constructor(object, lastObject, lastLastObject, difficultyHitObjects, clockRate, greatWindow
|
|
1500
|
+
constructor(object, lastObject, lastLastObject, difficultyHitObjects, clockRate, greatWindow) {
|
|
1498
1501
|
super(object, lastObject, lastLastObject, difficultyHitObjects, clockRate, greatWindow);
|
|
1499
1502
|
/**
|
|
1500
1503
|
* The tap strain generated by the hitobject.
|
|
@@ -1538,10 +1541,7 @@ class DroidDifficultyHitObject extends DifficultyHitObject {
|
|
|
1538
1541
|
this.radiusBuffThreshold = 70;
|
|
1539
1542
|
this.mode = osuBase.Modes.droid;
|
|
1540
1543
|
this.maximumSliderRadius = this.normalizedRadius * 2;
|
|
1541
|
-
this.timePreempt = object.timePreempt;
|
|
1542
|
-
if (!isForceAR) {
|
|
1543
|
-
this.timePreempt /= clockRate;
|
|
1544
|
-
}
|
|
1544
|
+
this.timePreempt = object.timePreempt / clockRate;
|
|
1545
1545
|
}
|
|
1546
1546
|
computeProperties(clockRate, hitObjects) {
|
|
1547
1547
|
super.computeProperties(clockRate, hitObjects);
|
|
@@ -1553,6 +1553,9 @@ class DroidDifficultyHitObject extends DifficultyHitObject {
|
|
|
1553
1553
|
* Keep in mind that "overlapping" in this case is overlapping to the point where both hitobjects
|
|
1554
1554
|
* can be hit with just a single tap in osu!droid.
|
|
1555
1555
|
*
|
|
1556
|
+
* In the case of sliders, it is considered overlapping if all nested hitobjects can be hit with
|
|
1557
|
+
* one aim motion.
|
|
1558
|
+
*
|
|
1556
1559
|
* @param considerDistance Whether to consider the distance between both hitobjects.
|
|
1557
1560
|
* @returns Whether the hitobject is considered overlapping.
|
|
1558
1561
|
*/
|
|
@@ -1560,23 +1563,48 @@ class DroidDifficultyHitObject extends DifficultyHitObject {
|
|
|
1560
1563
|
if (this.object instanceof osuBase.Spinner) {
|
|
1561
1564
|
return false;
|
|
1562
1565
|
}
|
|
1563
|
-
const
|
|
1564
|
-
if (!
|
|
1566
|
+
const prev = this.previous(0);
|
|
1567
|
+
if (!prev || prev.object instanceof osuBase.Spinner) {
|
|
1568
|
+
return false;
|
|
1569
|
+
}
|
|
1570
|
+
if (this.object.startTime !== prev.object.startTime) {
|
|
1565
1571
|
return false;
|
|
1566
1572
|
}
|
|
1567
|
-
if (
|
|
1573
|
+
if (!considerDistance) {
|
|
1574
|
+
return true;
|
|
1575
|
+
}
|
|
1576
|
+
const distanceThreshold = 2 * this.object.radius;
|
|
1577
|
+
const startPosition = this.object.getStackedPosition(osuBase.Modes.droid);
|
|
1578
|
+
const prevStartPosition = prev.object.getStackedPosition(osuBase.Modes.droid);
|
|
1579
|
+
// We need to consider two cases:
|
|
1580
|
+
//
|
|
1581
|
+
// Case 1: Current object is a circle, or previous object is a circle.
|
|
1582
|
+
// In this case, we only need to check if their positions are close enough to be tapped together.
|
|
1583
|
+
//
|
|
1584
|
+
// Case 2: Both objects are sliders.
|
|
1585
|
+
// In this case, we need to check if all nested hitobjects can be hit together.
|
|
1586
|
+
// To start with, check if the starting positions can be tapped together.
|
|
1587
|
+
if (startPosition.getDistance(prevStartPosition) > distanceThreshold) {
|
|
1568
1588
|
return false;
|
|
1569
1589
|
}
|
|
1570
|
-
if (
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1590
|
+
if (this.object instanceof osuBase.Circle || prev.object instanceof osuBase.Circle) {
|
|
1591
|
+
return true;
|
|
1592
|
+
}
|
|
1593
|
+
// Check if all nested hitobjects can be hit together.
|
|
1594
|
+
for (let i = 1; i < this.object.nestedHitObjects.length; ++i) {
|
|
1595
|
+
const position = this.object.nestedHitObjects[i].getStackedPosition(osuBase.Modes.droid);
|
|
1596
|
+
const prevPosition = prevStartPosition.add(prev.object.curvePositionAt(i / (this.object.nestedHitObjects.length - 1)));
|
|
1597
|
+
if (position.getDistance(prevPosition) > distanceThreshold) {
|
|
1598
|
+
return false;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
// Do the same for the previous slider as well.
|
|
1602
|
+
for (let i = 1; i < prev.object.nestedHitObjects.length; ++i) {
|
|
1603
|
+
const prevPosition = prev.object.nestedHitObjects[i].getStackedPosition(osuBase.Modes.droid);
|
|
1604
|
+
const position = startPosition.add(this.object.curvePositionAt(i / (prev.object.nestedHitObjects.length - 1)));
|
|
1605
|
+
if (prevPosition.getDistance(position) > distanceThreshold) {
|
|
1606
|
+
return false;
|
|
1578
1607
|
}
|
|
1579
|
-
return distance <= 2 * this.object.radius;
|
|
1580
1608
|
}
|
|
1581
1609
|
return true;
|
|
1582
1610
|
}
|
|
@@ -1660,7 +1688,6 @@ class DroidDifficultyCalculator extends DifficultyCalculator {
|
|
|
1660
1688
|
speedNoteCount: 0,
|
|
1661
1689
|
sliderFactor: 0,
|
|
1662
1690
|
clockRate: 1,
|
|
1663
|
-
approachRate: 0,
|
|
1664
1691
|
overallDifficulty: 0,
|
|
1665
1692
|
hitCircleCount: 0,
|
|
1666
1693
|
sliderCount: 0,
|
|
@@ -1710,30 +1737,11 @@ class DroidDifficultyCalculator extends DifficultyCalculator {
|
|
|
1710
1737
|
return this.attributes.visualDifficulty;
|
|
1711
1738
|
}
|
|
1712
1739
|
get cacheableAttributes() {
|
|
1713
|
-
return {
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
starRating: this.total,
|
|
1719
|
-
maxCombo: this.attributes.maxCombo,
|
|
1720
|
-
aimDifficulty: this.aim,
|
|
1721
|
-
flashlightDifficulty: this.flashlight,
|
|
1722
|
-
speedNoteCount: this.attributes.speedNoteCount,
|
|
1723
|
-
sliderFactor: this.attributes.sliderFactor,
|
|
1724
|
-
clockRate: this.attributes.clockRate,
|
|
1725
|
-
approachRate: this.attributes.approachRate,
|
|
1726
|
-
overallDifficulty: this.attributes.overallDifficulty,
|
|
1727
|
-
hitCircleCount: this.attributes.hitCircleCount,
|
|
1728
|
-
sliderCount: this.attributes.sliderCount,
|
|
1729
|
-
spinnerCount: this.attributes.spinnerCount,
|
|
1730
|
-
aimDifficultStrainCount: this.attributes.aimDifficultStrainCount,
|
|
1731
|
-
tapDifficultStrainCount: this.attributes.tapDifficultStrainCount,
|
|
1732
|
-
flashlightDifficultStrainCount: this.attributes.flashlightDifficultStrainCount,
|
|
1733
|
-
visualDifficultStrainCount: this.attributes.visualDifficultStrainCount,
|
|
1734
|
-
averageSpeedDeltaTime: this.attributes.averageSpeedDeltaTime,
|
|
1735
|
-
vibroFactor: this.attributes.vibroFactor,
|
|
1736
|
-
};
|
|
1740
|
+
return Object.assign(Object.assign({}, this.attributes), { mods: osuBase.ModUtil.modsToOsuString(this.attributes.mods) });
|
|
1741
|
+
}
|
|
1742
|
+
// Override to use DroidDifficultyCalculationOptions
|
|
1743
|
+
calculate(options) {
|
|
1744
|
+
return super.calculate(options);
|
|
1737
1745
|
}
|
|
1738
1746
|
/**
|
|
1739
1747
|
* Calculates the aim star rating of the beatmap and stores it in this instance.
|
|
@@ -1842,31 +1850,19 @@ class DroidDifficultyCalculator extends DifficultyCalculator {
|
|
|
1842
1850
|
this.visual.toFixed(2) +
|
|
1843
1851
|
" visual)");
|
|
1844
1852
|
}
|
|
1845
|
-
generateDifficultyHitObjects(
|
|
1853
|
+
generateDifficultyHitObjects(beatmap, clockRate) {
|
|
1846
1854
|
var _a, _b;
|
|
1847
1855
|
const difficultyObjects = [];
|
|
1848
|
-
const { objects } =
|
|
1849
|
-
const
|
|
1850
|
-
const greatWindow = new osuBase.
|
|
1856
|
+
const { objects } = beatmap.hitObjects;
|
|
1857
|
+
const isPrecise = this.mods.some((m) => m instanceof osuBase.ModPrecise);
|
|
1858
|
+
const greatWindow = new osuBase.DroidHitWindow(beatmap.difficulty.od).hitWindowFor300(isPrecise) / clockRate;
|
|
1851
1859
|
for (let i = 0; i < objects.length; ++i) {
|
|
1852
|
-
const difficultyObject = new DroidDifficultyHitObject(objects[i], (_a = objects[i - 1]) !== null && _a !== void 0 ? _a : null, (_b = objects[i - 2]) !== null && _b !== void 0 ? _b : null, difficultyObjects,
|
|
1853
|
-
difficultyObject.computeProperties(
|
|
1860
|
+
const difficultyObject = new DroidDifficultyHitObject(objects[i], (_a = objects[i - 1]) !== null && _a !== void 0 ? _a : null, (_b = objects[i - 2]) !== null && _b !== void 0 ? _b : null, difficultyObjects, clockRate, greatWindow);
|
|
1861
|
+
difficultyObject.computeProperties(clockRate, objects);
|
|
1854
1862
|
difficultyObjects.push(difficultyObject);
|
|
1855
1863
|
}
|
|
1856
1864
|
return difficultyObjects;
|
|
1857
1865
|
}
|
|
1858
|
-
computeDifficultyStatistics(options) {
|
|
1859
|
-
const { difficulty } = this.beatmap;
|
|
1860
|
-
return osuBase.calculateDroidDifficultyStatistics({
|
|
1861
|
-
circleSize: difficulty.cs,
|
|
1862
|
-
approachRate: difficulty.ar,
|
|
1863
|
-
overallDifficulty: difficulty.od,
|
|
1864
|
-
healthDrain: difficulty.hp,
|
|
1865
|
-
mods: this.mods,
|
|
1866
|
-
customSpeedMultiplier: options === null || options === void 0 ? void 0 : options.customSpeedMultiplier,
|
|
1867
|
-
oldStatistics: options === null || options === void 0 ? void 0 : options.oldStatistics,
|
|
1868
|
-
});
|
|
1869
|
-
}
|
|
1870
1866
|
createSkills() {
|
|
1871
1867
|
return [
|
|
1872
1868
|
new DroidAim(this.mods, true),
|
|
@@ -1883,6 +1879,10 @@ class DroidDifficultyCalculator extends DifficultyCalculator {
|
|
|
1883
1879
|
new DroidVisual(this.mods, false),
|
|
1884
1880
|
];
|
|
1885
1881
|
}
|
|
1882
|
+
calculateClockRate(options) {
|
|
1883
|
+
var _a, _b;
|
|
1884
|
+
return (osuBase.ModUtil.calculateRateWithMods((_a = options === null || options === void 0 ? void 0 : options.mods) !== null && _a !== void 0 ? _a : [], options === null || options === void 0 ? void 0 : options.oldStatistics) * ((_b = options === null || options === void 0 ? void 0 : options.customSpeedMultiplier) !== null && _b !== void 0 ? _b : 1));
|
|
1885
|
+
}
|
|
1886
1886
|
/**
|
|
1887
1887
|
* Called after aim skill calculation.
|
|
1888
1888
|
*
|
|
@@ -3030,7 +3030,7 @@ OsuSpeedEvaluator.SINGLE_SPACING_THRESHOLD = 125;
|
|
|
3030
3030
|
/**
|
|
3031
3031
|
* An evaluator for calculating osu!standard Rhythm skill.
|
|
3032
3032
|
*/
|
|
3033
|
-
class OsuRhythmEvaluator
|
|
3033
|
+
class OsuRhythmEvaluator {
|
|
3034
3034
|
/**
|
|
3035
3035
|
* Calculates a rhythm multiplier for the difficulty of the tap associated
|
|
3036
3036
|
* with historic data of the current object.
|
|
@@ -3136,6 +3136,8 @@ class OsuRhythmEvaluator extends RhythmEvaluator {
|
|
|
3136
3136
|
return Math.sqrt(4 + rhythmComplexitySum * this.rhythmMultiplier) / 2;
|
|
3137
3137
|
}
|
|
3138
3138
|
}
|
|
3139
|
+
OsuRhythmEvaluator.rhythmMultiplier = 0.75;
|
|
3140
|
+
OsuRhythmEvaluator.historyTimeMax = 5000; // 5 seconds of calculateRhythmBonus max.
|
|
3139
3141
|
|
|
3140
3142
|
/**
|
|
3141
3143
|
* Represents the skill required to press keys or tap with regards to keeping up with the speed at which objects need to be hit.
|
|
@@ -3447,29 +3449,19 @@ class OsuDifficultyCalculator extends DifficultyCalculator {
|
|
|
3447
3449
|
this.flashlight.toFixed(2) +
|
|
3448
3450
|
" flashlight)");
|
|
3449
3451
|
}
|
|
3450
|
-
generateDifficultyHitObjects(
|
|
3452
|
+
generateDifficultyHitObjects(beatmap, clockRate) {
|
|
3451
3453
|
var _a, _b;
|
|
3452
3454
|
const difficultyObjects = [];
|
|
3453
|
-
const { objects } =
|
|
3454
|
-
const greatWindow = new osuBase.OsuHitWindow(
|
|
3455
|
+
const { objects } = beatmap.hitObjects;
|
|
3456
|
+
const greatWindow = new osuBase.OsuHitWindow(beatmap.difficulty.od).hitWindowFor300() /
|
|
3457
|
+
clockRate;
|
|
3455
3458
|
for (let i = 0; i < objects.length; ++i) {
|
|
3456
|
-
const difficultyObject = new OsuDifficultyHitObject(objects[i], (_a = objects[i - 1]) !== null && _a !== void 0 ? _a : null, (_b = objects[i - 2]) !== null && _b !== void 0 ? _b : null, difficultyObjects,
|
|
3457
|
-
difficultyObject.computeProperties(
|
|
3459
|
+
const difficultyObject = new OsuDifficultyHitObject(objects[i], (_a = objects[i - 1]) !== null && _a !== void 0 ? _a : null, (_b = objects[i - 2]) !== null && _b !== void 0 ? _b : null, difficultyObjects, clockRate, greatWindow);
|
|
3460
|
+
difficultyObject.computeProperties(clockRate, objects);
|
|
3458
3461
|
difficultyObjects.push(difficultyObject);
|
|
3459
3462
|
}
|
|
3460
3463
|
return difficultyObjects;
|
|
3461
3464
|
}
|
|
3462
|
-
computeDifficultyStatistics(options) {
|
|
3463
|
-
const { difficulty } = this.beatmap;
|
|
3464
|
-
return osuBase.calculateOsuDifficultyStatistics({
|
|
3465
|
-
circleSize: difficulty.cs,
|
|
3466
|
-
approachRate: difficulty.ar,
|
|
3467
|
-
overallDifficulty: difficulty.od,
|
|
3468
|
-
healthDrain: difficulty.hp,
|
|
3469
|
-
mods: options === null || options === void 0 ? void 0 : options.mods,
|
|
3470
|
-
customSpeedMultiplier: options === null || options === void 0 ? void 0 : options.customSpeedMultiplier,
|
|
3471
|
-
});
|
|
3472
|
-
}
|
|
3473
3465
|
createSkills() {
|
|
3474
3466
|
return [
|
|
3475
3467
|
new OsuAim(this.mods, true),
|
|
@@ -3478,6 +3470,11 @@ class OsuDifficultyCalculator extends DifficultyCalculator {
|
|
|
3478
3470
|
new OsuFlashlight(this.mods),
|
|
3479
3471
|
];
|
|
3480
3472
|
}
|
|
3473
|
+
populateDifficultyAttributes(beatmap, clockRate) {
|
|
3474
|
+
super.populateDifficultyAttributes(beatmap, clockRate);
|
|
3475
|
+
const preempt = osuBase.BeatmapDifficulty.difficultyRange(beatmap.difficulty.ar, osuBase.HitObject.preemptMax, osuBase.HitObject.preemptMid, osuBase.HitObject.preemptMin) / clockRate;
|
|
3476
|
+
this.attributes.approachRate = osuBase.BeatmapDifficulty.inverseDifficultyRange(preempt, osuBase.HitObject.preemptMax, osuBase.HitObject.preemptMid, osuBase.HitObject.preemptMin);
|
|
3477
|
+
}
|
|
3481
3478
|
/**
|
|
3482
3479
|
* Called after aim skill calculation.
|
|
3483
3480
|
*
|
|
@@ -3784,6 +3781,5 @@ exports.OsuRhythmEvaluator = OsuRhythmEvaluator;
|
|
|
3784
3781
|
exports.OsuSpeed = OsuSpeed;
|
|
3785
3782
|
exports.OsuSpeedEvaluator = OsuSpeedEvaluator;
|
|
3786
3783
|
exports.PerformanceCalculator = PerformanceCalculator;
|
|
3787
|
-
exports.RhythmEvaluator = RhythmEvaluator;
|
|
3788
3784
|
exports.SpeedEvaluator = SpeedEvaluator;
|
|
3789
3785
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rian8337/osu-difficulty-calculator",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.27",
|
|
4
4
|
"description": "A module for calculating osu!standard beatmap difficulty and performance value with respect to the current difficulty and performance algorithm.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"osu",
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
"url": "https://github.com/Rian8337/osu-droid-module/issues"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@rian8337/osu-base": "^4.0.0-beta.
|
|
36
|
+
"@rian8337/osu-base": "^4.0.0-beta.27"
|
|
37
37
|
},
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "6714ebe03b132a0f9406a72900af70a9c39a58bc"
|
|
42
42
|
}
|
package/typings/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Mod, PlaceableHitObject, Modes, Beatmap,
|
|
1
|
+
import { Mod, PlaceableHitObject, Modes, Beatmap, Accuracy } from '@rian8337/osu-base';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* An evaluator for calculating aim skill.
|
|
@@ -62,12 +62,6 @@ interface DifficultyAttributes {
|
|
|
62
62
|
* The overall clock rate that was applied to the beatmap.
|
|
63
63
|
*/
|
|
64
64
|
clockRate: number;
|
|
65
|
-
/**
|
|
66
|
-
* The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
|
|
67
|
-
*
|
|
68
|
-
* Rate-adjusting mods don't directly affect the approach rate difficulty value, but have a perceived effect as a result of adjusting audio timing.
|
|
69
|
-
*/
|
|
70
|
-
approachRate: number;
|
|
71
65
|
/**
|
|
72
66
|
* The perceived overall difficulty inclusive of rate-adjusting mods (DT/HT/etc), based on osu!standard judgement.
|
|
73
67
|
*
|
|
@@ -327,7 +321,11 @@ declare abstract class DifficultyCalculator<THitObject extends DifficultyHitObje
|
|
|
327
321
|
/**
|
|
328
322
|
* The difficulty objects of the beatmap.
|
|
329
323
|
*/
|
|
330
|
-
|
|
324
|
+
private _objects;
|
|
325
|
+
/**
|
|
326
|
+
* The difficulty objects of the beatmap.
|
|
327
|
+
*/
|
|
328
|
+
get objects(): readonly THitObject[];
|
|
331
329
|
/**
|
|
332
330
|
* The modifications applied.
|
|
333
331
|
*/
|
|
@@ -336,10 +334,6 @@ declare abstract class DifficultyCalculator<THitObject extends DifficultyHitObje
|
|
|
336
334
|
* The total star rating of the beatmap.
|
|
337
335
|
*/
|
|
338
336
|
get total(): number;
|
|
339
|
-
/**
|
|
340
|
-
* The difficulty statistics of the beatmap after modifications are applied.
|
|
341
|
-
*/
|
|
342
|
-
difficultyStatistics: DifficultyStatisticsCalculatorResult<number, number, number, number>;
|
|
343
337
|
/**
|
|
344
338
|
* The strain peaks of various calculated difficulties.
|
|
345
339
|
*/
|
|
@@ -357,7 +351,7 @@ declare abstract class DifficultyCalculator<THitObject extends DifficultyHitObje
|
|
|
357
351
|
/**
|
|
358
352
|
* Constructs a new instance of the calculator.
|
|
359
353
|
*
|
|
360
|
-
* @param beatmap The beatmap to calculate.
|
|
354
|
+
* @param beatmap The beatmap to calculate.
|
|
361
355
|
*/
|
|
362
356
|
constructor(beatmap: Beatmap);
|
|
363
357
|
/**
|
|
@@ -379,16 +373,10 @@ declare abstract class DifficultyCalculator<THitObject extends DifficultyHitObje
|
|
|
379
373
|
/**
|
|
380
374
|
* Generates difficulty hitobjects for this calculator.
|
|
381
375
|
*
|
|
382
|
-
* @param
|
|
383
|
-
|
|
384
|
-
protected abstract generateDifficultyHitObjects(convertedBeatmap: Beatmap): THitObject[];
|
|
385
|
-
/**
|
|
386
|
-
* Computes the difficulty statistics of the original beatmap with respect to the used options.
|
|
387
|
-
*
|
|
388
|
-
* @param options The options to use for the difficulty statistics calculation.
|
|
389
|
-
* @returns The computed difficulty statistics.
|
|
376
|
+
* @param beatmap The beatmap to generate difficulty hitobjects from.
|
|
377
|
+
* @param clockRate The clock rate of the beatmap.
|
|
390
378
|
*/
|
|
391
|
-
protected abstract
|
|
379
|
+
protected abstract generateDifficultyHitObjects(beatmap: Beatmap, clockRate: number): THitObject[];
|
|
392
380
|
/**
|
|
393
381
|
* Calculates the skills provided.
|
|
394
382
|
*
|
|
@@ -411,10 +399,20 @@ declare abstract class DifficultyCalculator<THitObject extends DifficultyHitObje
|
|
|
411
399
|
* Creates skills to be calculated.
|
|
412
400
|
*/
|
|
413
401
|
protected abstract createSkills(): Skill[];
|
|
402
|
+
/**
|
|
403
|
+
* Obtains the clock rate of the beatmap.
|
|
404
|
+
*
|
|
405
|
+
* @param options The options to obtain the clock rate with.
|
|
406
|
+
* @returns The clock rate of the beatmap.
|
|
407
|
+
*/
|
|
408
|
+
protected calculateClockRate(options?: DifficultyCalculationOptions): number;
|
|
414
409
|
/**
|
|
415
410
|
* Populates the stored difficulty attributes with necessary data.
|
|
411
|
+
*
|
|
412
|
+
* @param beatmap The beatmap to populate the attributes with.
|
|
413
|
+
* @param clockRate The clock rate of the beatmap.
|
|
416
414
|
*/
|
|
417
|
-
protected populateDifficultyAttributes(): void;
|
|
415
|
+
protected populateDifficultyAttributes(beatmap: Beatmap, clockRate: number): void;
|
|
418
416
|
/**
|
|
419
417
|
* Calculates the star rating value of a difficulty.
|
|
420
418
|
*
|
|
@@ -616,9 +614,8 @@ declare class DroidDifficultyHitObject extends DifficultyHitObject {
|
|
|
616
614
|
* @param difficultyHitObjects All difficulty hitobjects in the processed beatmap.
|
|
617
615
|
* @param clockRate The clock rate of the beatmap.
|
|
618
616
|
* @param greatWindow The great window of the hitobject.
|
|
619
|
-
* @param isForceAR Whether force AR is enabled.
|
|
620
617
|
*/
|
|
621
|
-
constructor(object: PlaceableHitObject, lastObject: PlaceableHitObject | null, lastLastObject: PlaceableHitObject | null, difficultyHitObjects: readonly DifficultyHitObject[], clockRate: number, greatWindow: number
|
|
618
|
+
constructor(object: PlaceableHitObject, lastObject: PlaceableHitObject | null, lastLastObject: PlaceableHitObject | null, difficultyHitObjects: readonly DifficultyHitObject[], clockRate: number, greatWindow: number);
|
|
622
619
|
computeProperties(clockRate: number, hitObjects: readonly PlaceableHitObject[]): void;
|
|
623
620
|
/**
|
|
624
621
|
* Determines whether this hitobject is considered overlapping with the hitobject before it.
|
|
@@ -626,6 +623,9 @@ declare class DroidDifficultyHitObject extends DifficultyHitObject {
|
|
|
626
623
|
* Keep in mind that "overlapping" in this case is overlapping to the point where both hitobjects
|
|
627
624
|
* can be hit with just a single tap in osu!droid.
|
|
628
625
|
*
|
|
626
|
+
* In the case of sliders, it is considered overlapping if all nested hitobjects can be hit with
|
|
627
|
+
* one aim motion.
|
|
628
|
+
*
|
|
629
629
|
* @param considerDistance Whether to consider the distance between both hitobjects.
|
|
630
630
|
* @returns Whether the hitobject is considered overlapping.
|
|
631
631
|
*/
|
|
@@ -835,6 +835,7 @@ declare class DroidDifficultyCalculator extends DifficultyCalculator<DroidDiffic
|
|
|
835
835
|
get cacheableAttributes(): CacheableDifficultyAttributes<DroidDifficultyAttributes>;
|
|
836
836
|
protected readonly difficultyMultiplier = 0.18;
|
|
837
837
|
protected readonly mode = Modes.droid;
|
|
838
|
+
calculate(options?: DroidDifficultyCalculationOptions): this;
|
|
838
839
|
/**
|
|
839
840
|
* Calculates the aim star rating of the beatmap and stores it in this instance.
|
|
840
841
|
*/
|
|
@@ -858,9 +859,9 @@ declare class DroidDifficultyCalculator extends DifficultyCalculator<DroidDiffic
|
|
|
858
859
|
calculateTotal(): void;
|
|
859
860
|
calculateAll(): void;
|
|
860
861
|
toString(): string;
|
|
861
|
-
protected generateDifficultyHitObjects(
|
|
862
|
-
protected computeDifficultyStatistics(options?: DroidDifficultyCalculationOptions): DifficultyStatisticsCalculatorResult<number, number, number, number>;
|
|
862
|
+
protected generateDifficultyHitObjects(beatmap: Beatmap, clockRate: number): DroidDifficultyHitObject[];
|
|
863
863
|
protected createSkills(): DroidSkill[];
|
|
864
|
+
protected calculateClockRate(options?: DroidDifficultyCalculationOptions): number;
|
|
864
865
|
/**
|
|
865
866
|
* Called after aim skill calculation.
|
|
866
867
|
*
|
|
@@ -1266,24 +1267,14 @@ declare class DroidRhythm extends DroidSkill {
|
|
|
1266
1267
|
protected saveToHitObject(current: DroidDifficultyHitObject): void;
|
|
1267
1268
|
}
|
|
1268
1269
|
|
|
1269
|
-
/**
|
|
1270
|
-
* An evaluator for calculating rhythm skill.
|
|
1271
|
-
*
|
|
1272
|
-
* This class should be considered an "evaluating" class and not persisted.
|
|
1273
|
-
*/
|
|
1274
|
-
declare abstract class RhythmEvaluator {
|
|
1275
|
-
protected static readonly rhythmMultiplier: number;
|
|
1276
|
-
protected static readonly historyTimeMax: number;
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
1270
|
/**
|
|
1280
1271
|
* An evaluator for calculating osu!droid Rhythm skill.
|
|
1281
1272
|
*/
|
|
1282
|
-
declare abstract class DroidRhythmEvaluator
|
|
1283
|
-
|
|
1284
|
-
protected static readonly historyTimeMax = 4000;
|
|
1285
|
-
private static readonly maxIslandSize;
|
|
1273
|
+
declare abstract class DroidRhythmEvaluator {
|
|
1274
|
+
private static readonly historyTimeMax;
|
|
1286
1275
|
private static readonly historyObjectsMax;
|
|
1276
|
+
private static readonly rhythmOverallMultiplier;
|
|
1277
|
+
private static readonly rhythmRatioMultiplier;
|
|
1287
1278
|
/**
|
|
1288
1279
|
* Calculates a rhythm multiplier for the difficulty of the tap associated
|
|
1289
1280
|
* with historic data of the current object.
|
|
@@ -1481,6 +1472,12 @@ declare abstract class OsuAimEvaluator extends AimEvaluator {
|
|
|
1481
1472
|
* Holds data that can be used to calculate osu!standard performance points.
|
|
1482
1473
|
*/
|
|
1483
1474
|
interface OsuDifficultyAttributes extends DifficultyAttributes {
|
|
1475
|
+
/**
|
|
1476
|
+
* The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
|
|
1477
|
+
*
|
|
1478
|
+
* Rate-adjusting mods don't directly affect the approach rate difficulty value, but have a perceived effect as a result of adjusting audio timing.
|
|
1479
|
+
*/
|
|
1480
|
+
approachRate: number;
|
|
1484
1481
|
/**
|
|
1485
1482
|
* The difficulty corresponding to the speed skill.
|
|
1486
1483
|
*/
|
|
@@ -1522,9 +1519,9 @@ declare class OsuDifficultyCalculator extends DifficultyCalculator<OsuDifficulty
|
|
|
1522
1519
|
calculateTotal(): void;
|
|
1523
1520
|
calculateAll(): void;
|
|
1524
1521
|
toString(): string;
|
|
1525
|
-
protected generateDifficultyHitObjects(
|
|
1526
|
-
protected computeDifficultyStatistics(options?: DifficultyCalculationOptions): DifficultyStatisticsCalculatorResult<number, number, number, number>;
|
|
1522
|
+
protected generateDifficultyHitObjects(beatmap: Beatmap, clockRate: number): OsuDifficultyHitObject[];
|
|
1527
1523
|
protected createSkills(): OsuSkill[];
|
|
1524
|
+
protected populateDifficultyAttributes(beatmap: Beatmap, clockRate: number): void;
|
|
1528
1525
|
/**
|
|
1529
1526
|
* Called after aim skill calculation.
|
|
1530
1527
|
*
|
|
@@ -1632,7 +1629,9 @@ declare class OsuPerformanceCalculator extends PerformanceCalculator<OsuDifficul
|
|
|
1632
1629
|
/**
|
|
1633
1630
|
* An evaluator for calculating osu!standard Rhythm skill.
|
|
1634
1631
|
*/
|
|
1635
|
-
declare abstract class OsuRhythmEvaluator
|
|
1632
|
+
declare abstract class OsuRhythmEvaluator {
|
|
1633
|
+
private static readonly rhythmMultiplier;
|
|
1634
|
+
private static readonly historyTimeMax;
|
|
1636
1635
|
/**
|
|
1637
1636
|
* Calculates a rhythm multiplier for the difficulty of the tap associated
|
|
1638
1637
|
* with historic data of the current object.
|
|
@@ -1686,4 +1685,4 @@ declare abstract class OsuSpeedEvaluator extends SpeedEvaluator {
|
|
|
1686
1685
|
static evaluateDifficultyOf(current: OsuDifficultyHitObject): number;
|
|
1687
1686
|
}
|
|
1688
1687
|
|
|
1689
|
-
export { AimEvaluator, type CacheableDifficultyAttributes, type DifficultSlider, type DifficultyAttributes, type DifficultyCalculationOptions, DifficultyCalculator, DifficultyHitObject, DroidAim, DroidAimEvaluator, type DroidDifficultyAttributes, type DroidDifficultyCalculationOptions, DroidDifficultyCalculator, DroidDifficultyHitObject, DroidFlashlight, DroidFlashlightEvaluator, DroidPerformanceCalculator, DroidRhythm, DroidRhythmEvaluator, DroidTap, DroidTapEvaluator, DroidVisual, DroidVisualEvaluator, type ExtendedDroidDifficultyAttributes, FlashlightEvaluator, type HighStrainSection, OsuAim, OsuAimEvaluator, type OsuDifficultyAttributes, OsuDifficultyCalculator, OsuDifficultyHitObject, OsuFlashlight, OsuFlashlightEvaluator, OsuPerformanceCalculator, OsuRhythmEvaluator, OsuSpeed, OsuSpeedEvaluator, type PerformanceCalculationOptions, PerformanceCalculator,
|
|
1688
|
+
export { AimEvaluator, type CacheableDifficultyAttributes, type DifficultSlider, type DifficultyAttributes, type DifficultyCalculationOptions, DifficultyCalculator, DifficultyHitObject, DroidAim, DroidAimEvaluator, type DroidDifficultyAttributes, type DroidDifficultyCalculationOptions, DroidDifficultyCalculator, DroidDifficultyHitObject, DroidFlashlight, DroidFlashlightEvaluator, DroidPerformanceCalculator, DroidRhythm, DroidRhythmEvaluator, DroidTap, DroidTapEvaluator, DroidVisual, DroidVisualEvaluator, type ExtendedDroidDifficultyAttributes, FlashlightEvaluator, type HighStrainSection, OsuAim, OsuAimEvaluator, type OsuDifficultyAttributes, OsuDifficultyCalculator, OsuDifficultyHitObject, OsuFlashlight, OsuFlashlightEvaluator, OsuPerformanceCalculator, OsuRhythmEvaluator, OsuSpeed, OsuSpeedEvaluator, type PerformanceCalculationOptions, PerformanceCalculator, SpeedEvaluator, type StrainPeaks };
|