@rian8337/osu-difficulty-calculator 4.0.0-beta.25 → 4.0.0-beta.26
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 +161 -183
- package/package.json +3 -3
- package/typings/index.d.ts +35 -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.
|
|
@@ -86,16 +85,17 @@ class DifficultyCalculator {
|
|
|
86
85
|
* @returns The current instance.
|
|
87
86
|
*/
|
|
88
87
|
calculate(options) {
|
|
89
|
-
var _a;
|
|
88
|
+
var _a, _b;
|
|
90
89
|
this.mods = (_a = options === null || options === void 0 ? void 0 : options.mods) !== null && _a !== void 0 ? _a : [];
|
|
91
90
|
const playableBeatmap = this.beatmap.createPlayableBeatmap({
|
|
92
91
|
mode: this.mode,
|
|
93
92
|
mods: this.mods,
|
|
94
93
|
customSpeedMultiplier: options === null || options === void 0 ? void 0 : options.customSpeedMultiplier,
|
|
95
94
|
});
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
this.
|
|
95
|
+
const clockRate = osuBase.ModUtil.calculateRateWithMods(this.mods) *
|
|
96
|
+
((_b = options === null || options === void 0 ? void 0 : options.customSpeedMultiplier) !== null && _b !== void 0 ? _b : 1);
|
|
97
|
+
this.populateDifficultyAttributes(playableBeatmap, clockRate);
|
|
98
|
+
this._objects = this.generateDifficultyHitObjects(playableBeatmap, clockRate);
|
|
99
99
|
this.calculateAll();
|
|
100
100
|
return this;
|
|
101
101
|
}
|
|
@@ -114,18 +114,27 @@ class DifficultyCalculator {
|
|
|
114
114
|
}
|
|
115
115
|
/**
|
|
116
116
|
* Populates the stored difficulty attributes with necessary data.
|
|
117
|
+
*
|
|
118
|
+
* @param beatmap The beatmap to populate the attributes with.
|
|
119
|
+
* @param clockRate The clock rate of the beatmap.
|
|
117
120
|
*/
|
|
118
|
-
populateDifficultyAttributes() {
|
|
119
|
-
this.attributes.approachRate = this.difficultyStatistics.approachRate;
|
|
121
|
+
populateDifficultyAttributes(beatmap, clockRate) {
|
|
120
122
|
this.attributes.hitCircleCount = this.beatmap.hitObjects.circles;
|
|
121
123
|
this.attributes.maxCombo = this.beatmap.maxCombo;
|
|
122
124
|
this.attributes.mods = this.mods.slice();
|
|
123
|
-
this.attributes.overallDifficulty =
|
|
124
|
-
this.difficultyStatistics.overallDifficulty;
|
|
125
125
|
this.attributes.sliderCount = this.beatmap.hitObjects.sliders;
|
|
126
126
|
this.attributes.spinnerCount = this.beatmap.hitObjects.spinners;
|
|
127
|
-
this.attributes.clockRate =
|
|
128
|
-
|
|
127
|
+
this.attributes.clockRate = clockRate;
|
|
128
|
+
let greatWindow;
|
|
129
|
+
switch (this.mode) {
|
|
130
|
+
case osuBase.Modes.droid:
|
|
131
|
+
greatWindow = new osuBase.DroidHitWindow(beatmap.difficulty.od).hitWindowFor300(this.mods.some((m) => m instanceof osuBase.ModPrecise));
|
|
132
|
+
break;
|
|
133
|
+
case osuBase.Modes.osu:
|
|
134
|
+
greatWindow = new osuBase.OsuHitWindow(beatmap.difficulty.od).hitWindowFor300();
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
this.attributes.overallDifficulty = osuBase.OsuHitWindow.hitWindow300ToOD(greatWindow / clockRate);
|
|
129
138
|
}
|
|
130
139
|
/**
|
|
131
140
|
* Calculates the star rating value of a difficulty.
|
|
@@ -1057,7 +1066,7 @@ class DroidFlashlight extends DroidSkill {
|
|
|
1057
1066
|
this.reducedSectionCount = 0;
|
|
1058
1067
|
this.reducedSectionBaseline = 1;
|
|
1059
1068
|
this.starsPerDouble = 1.06;
|
|
1060
|
-
this.skillMultiplier = 0.
|
|
1069
|
+
this.skillMultiplier = 0.02;
|
|
1061
1070
|
this.currentFlashlightStrain = 0;
|
|
1062
1071
|
this.isHidden = mods.some((m) => m instanceof osuBase.ModHidden);
|
|
1063
1072
|
this.withSliders = withSliders;
|
|
@@ -1086,64 +1095,42 @@ class DroidFlashlight extends DroidSkill {
|
|
|
1086
1095
|
}
|
|
1087
1096
|
}
|
|
1088
1097
|
difficultyValue() {
|
|
1089
|
-
return
|
|
1098
|
+
return (this.strainPeaks.reduce((a, v) => a + v, 0) * this.starsPerDouble);
|
|
1090
1099
|
}
|
|
1091
1100
|
}
|
|
1092
1101
|
|
|
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
1102
|
class Island {
|
|
1104
|
-
constructor(
|
|
1105
|
-
this.
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1103
|
+
constructor(delta, deltaDifferenceEpsilon) {
|
|
1104
|
+
this.delta = Number.MAX_SAFE_INTEGER;
|
|
1105
|
+
this.deltaCount = 0;
|
|
1106
|
+
if (deltaDifferenceEpsilon === undefined) {
|
|
1107
|
+
this.deltaDifferenceEpsilon = delta;
|
|
1108
|
+
}
|
|
1109
|
+
else {
|
|
1110
|
+
this.deltaDifferenceEpsilon = deltaDifferenceEpsilon;
|
|
1111
|
+
this.addDelta(delta);
|
|
1109
1112
|
}
|
|
1110
|
-
this.deltaDifferenceEpsilon = epsilon;
|
|
1111
|
-
this.addDelta(firstDelta);
|
|
1112
1113
|
}
|
|
1113
1114
|
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;
|
|
1115
|
+
if (this.delta === Number.MAX_SAFE_INTEGER) {
|
|
1116
|
+
this.delta = Math.max(Math.trunc(delta), DifficultyHitObject.minDeltaTime);
|
|
1117
|
+
}
|
|
1118
|
+
++this.deltaCount;
|
|
1123
1119
|
}
|
|
1124
1120
|
isSimilarPolarity(other) {
|
|
1125
|
-
//
|
|
1126
|
-
//
|
|
1127
|
-
return
|
|
1128
|
-
this.deltaDifferenceEpsilon &&
|
|
1129
|
-
this.deltas.length % 2 === other.deltas.length % 2);
|
|
1121
|
+
// 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)
|
|
1122
|
+
// naively adding delta check here breaks _a lot_ of maps because of the flawed ratio calculation
|
|
1123
|
+
return this.deltaCount % 2 == other.deltaCount % 2;
|
|
1130
1124
|
}
|
|
1131
1125
|
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;
|
|
1126
|
+
return (Math.abs(this.delta - other.delta) < this.deltaDifferenceEpsilon &&
|
|
1127
|
+
this.deltaCount === other.deltaCount);
|
|
1141
1128
|
}
|
|
1142
1129
|
}
|
|
1143
1130
|
/**
|
|
1144
1131
|
* An evaluator for calculating osu!droid Rhythm skill.
|
|
1145
1132
|
*/
|
|
1146
|
-
class DroidRhythmEvaluator
|
|
1133
|
+
class DroidRhythmEvaluator {
|
|
1147
1134
|
/**
|
|
1148
1135
|
* Calculates a rhythm multiplier for the difficulty of the tap associated
|
|
1149
1136
|
* with historic data of the current object.
|
|
@@ -1151,9 +1138,7 @@ class DroidRhythmEvaluator extends RhythmEvaluator {
|
|
|
1151
1138
|
* @param current The current object.
|
|
1152
1139
|
*/
|
|
1153
1140
|
static evaluateDifficultyOf(current) {
|
|
1154
|
-
if (current.object instanceof osuBase.Spinner
|
|
1155
|
-
// Exclude overlapping objects that can be tapped at once.
|
|
1156
|
-
current.isOverlapping(false)) {
|
|
1141
|
+
if (current.object instanceof osuBase.Spinner) {
|
|
1157
1142
|
return 1;
|
|
1158
1143
|
}
|
|
1159
1144
|
const deltaDifferenceEpsilon = current.fullGreatWindow * 0.3;
|
|
@@ -1183,47 +1168,50 @@ class DroidRhythmEvaluator extends RhythmEvaluator {
|
|
|
1183
1168
|
++rhythmStart;
|
|
1184
1169
|
}
|
|
1185
1170
|
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
1171
|
const currentObject = validPrevious[i - 1];
|
|
1193
1172
|
const prevObject = validPrevious[i];
|
|
1194
1173
|
const lastObject = validPrevious[i + 1];
|
|
1174
|
+
// Scale note 0 to 1 from history to now.
|
|
1175
|
+
const timeDecay = (this.historyTimeMax -
|
|
1176
|
+
(current.startTime - currentObject.startTime)) /
|
|
1177
|
+
this.historyTimeMax;
|
|
1178
|
+
const noteDecay = (validPrevious.length - i) / validPrevious.length;
|
|
1179
|
+
// Either we're limited by time or limited by object count.
|
|
1180
|
+
const currentHistoricalDecay = Math.min(timeDecay, noteDecay);
|
|
1195
1181
|
const currentDelta = currentObject.strainTime;
|
|
1196
1182
|
const prevDelta = prevObject.strainTime;
|
|
1197
1183
|
const lastDelta = lastObject.strainTime;
|
|
1184
|
+
// Calculate how much current delta difference deserves a rhythm bonus
|
|
1185
|
+
// This function is meant to reduce rhythm bonus for deltas that are multiples of each other (i.e. 100 and 200)
|
|
1186
|
+
const deltaDifferenceRatio = Math.min(prevDelta, currentDelta) /
|
|
1187
|
+
Math.max(prevDelta, currentDelta);
|
|
1198
1188
|
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;
|
|
1189
|
+
this.rhythmRatioMultiplier *
|
|
1190
|
+
Math.min(0.5, Math.pow(Math.sin(Math.PI / deltaDifferenceRatio), 2));
|
|
1191
|
+
// Reduce ratio bonus if delta difference is too big
|
|
1192
|
+
const fraction = Math.max(prevDelta / currentDelta, currentDelta / prevDelta);
|
|
1193
|
+
const fractionMultiplier = osuBase.MathUtils.clamp(2 - fraction / 8, 0, 1);
|
|
1194
|
+
const windowPenalty = Math.min(1, Math.max(0, Math.abs(prevDelta - currentDelta) - deltaDifferenceEpsilon) / deltaDifferenceEpsilon);
|
|
1195
|
+
let effectiveRatio = windowPenalty * currentRatio * fractionMultiplier;
|
|
1206
1196
|
if (firstDeltaSwitch) {
|
|
1207
|
-
if (Math.abs(prevDelta - currentDelta)
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
island.addDelta(currentDelta);
|
|
1211
|
-
}
|
|
1197
|
+
if (Math.abs(prevDelta - currentDelta) < deltaDifferenceEpsilon) {
|
|
1198
|
+
// Island is still progressing, count size.
|
|
1199
|
+
island.addDelta(currentDelta);
|
|
1212
1200
|
}
|
|
1213
1201
|
else {
|
|
1214
1202
|
// BPM change is into slider, this is easy acc window.
|
|
1215
1203
|
if (currentObject.object instanceof osuBase.Slider) {
|
|
1216
1204
|
effectiveRatio /= 8;
|
|
1217
1205
|
}
|
|
1218
|
-
// BPM change was from a slider, this is typically
|
|
1219
|
-
// Unintentional side effect is that bursts with kicksliders at the ends might
|
|
1220
|
-
//
|
|
1206
|
+
// BPM change was from a slider, this is easier typically than circle -> circle.
|
|
1207
|
+
// Unintentional side effect is that bursts with kicksliders at the ends might have lower difficulty
|
|
1208
|
+
// than bursts without sliders.
|
|
1221
1209
|
if (prevObject.object instanceof osuBase.Slider) {
|
|
1222
|
-
effectiveRatio
|
|
1210
|
+
effectiveRatio *= 0.3;
|
|
1223
1211
|
}
|
|
1224
1212
|
// Repeated island polarity (2 -> 4, 3 -> 5).
|
|
1225
1213
|
if (island.isSimilarPolarity(previousIsland)) {
|
|
1226
|
-
effectiveRatio
|
|
1214
|
+
effectiveRatio /= 2;
|
|
1227
1215
|
}
|
|
1228
1216
|
// Previous increase happened a note ago.
|
|
1229
1217
|
// Albeit this is a 1/1 -> 1/2-1/4 type of transition, we don't want to buff this.
|
|
@@ -1231,8 +1219,9 @@ class DroidRhythmEvaluator extends RhythmEvaluator {
|
|
|
1231
1219
|
prevDelta > currentDelta + deltaDifferenceEpsilon) {
|
|
1232
1220
|
effectiveRatio /= 8;
|
|
1233
1221
|
}
|
|
1234
|
-
//
|
|
1235
|
-
|
|
1222
|
+
// Repeated island size (ex: triplet -> triplet).
|
|
1223
|
+
// TODO: remove this nerf since its staying here only for balancing purposes because of the flawed ratio calculation
|
|
1224
|
+
if (previousIsland.deltaCount == island.deltaCount) {
|
|
1236
1225
|
effectiveRatio /= 2;
|
|
1237
1226
|
}
|
|
1238
1227
|
let islandFound = false;
|
|
@@ -1249,9 +1238,7 @@ class DroidRhythmEvaluator extends RhythmEvaluator {
|
|
|
1249
1238
|
}
|
|
1250
1239
|
// Repeated island (ex: triplet -> triplet).
|
|
1251
1240
|
// Graph: https://www.desmos.com/calculator/pj7an56zwf
|
|
1252
|
-
effectiveRatio *= Math.min(
|
|
1253
|
-
(1 +
|
|
1254
|
-
Math.exp(10 - 0.165 * island.averageDelta))));
|
|
1241
|
+
effectiveRatio *= Math.min(3 / islandCount, Math.pow(1 / islandCount, 2.75 / (1 + Math.exp(14 - 0.24 * island.delta))));
|
|
1255
1242
|
break;
|
|
1256
1243
|
}
|
|
1257
1244
|
if (!islandFound) {
|
|
@@ -1269,28 +1256,35 @@ class DroidRhythmEvaluator extends RhythmEvaluator {
|
|
|
1269
1256
|
// If we're speeding up, this stays as is and we keep counting island size.
|
|
1270
1257
|
firstDeltaSwitch = false;
|
|
1271
1258
|
}
|
|
1272
|
-
island = new Island(
|
|
1259
|
+
island = new Island(currentDelta, deltaDifferenceEpsilon);
|
|
1273
1260
|
}
|
|
1274
1261
|
}
|
|
1275
|
-
else if (prevDelta >
|
|
1276
|
-
// We
|
|
1262
|
+
else if (prevDelta > currentDelta + deltaDifferenceEpsilon) {
|
|
1263
|
+
// We are speeding up.
|
|
1277
1264
|
// Begin counting island until we change speed again.
|
|
1278
1265
|
firstDeltaSwitch = true;
|
|
1279
|
-
//
|
|
1266
|
+
// BPM change is into slider, this is easy acc window.
|
|
1267
|
+
if (currentObject.object instanceof osuBase.Slider) {
|
|
1268
|
+
effectiveRatio *= 0.6;
|
|
1269
|
+
}
|
|
1270
|
+
// BPM change was from a slider, this is easier typically than circle -> circle
|
|
1271
|
+
// Unintentional side effect is that bursts with kicksliders at the ends might have lower difficulty
|
|
1272
|
+
// than bursts without sliders
|
|
1280
1273
|
if (prevObject.object instanceof osuBase.Slider) {
|
|
1281
|
-
effectiveRatio *= 0.
|
|
1274
|
+
effectiveRatio *= 0.6;
|
|
1282
1275
|
}
|
|
1283
1276
|
startRatio = effectiveRatio;
|
|
1284
|
-
island = new Island(
|
|
1277
|
+
island = new Island(currentDelta, deltaDifferenceEpsilon);
|
|
1285
1278
|
}
|
|
1286
1279
|
}
|
|
1287
|
-
return Math.sqrt(4 + rhythmComplexitySum * this.
|
|
1280
|
+
return (Math.sqrt(4 + rhythmComplexitySum * this.rhythmOverallMultiplier) /
|
|
1281
|
+
2);
|
|
1288
1282
|
}
|
|
1289
1283
|
}
|
|
1290
|
-
DroidRhythmEvaluator.
|
|
1291
|
-
DroidRhythmEvaluator.
|
|
1292
|
-
DroidRhythmEvaluator.
|
|
1293
|
-
DroidRhythmEvaluator.
|
|
1284
|
+
DroidRhythmEvaluator.historyTimeMax = 5000; // 5 seconds of calculateRhythmBonus max.
|
|
1285
|
+
DroidRhythmEvaluator.historyObjectsMax = 32;
|
|
1286
|
+
DroidRhythmEvaluator.rhythmOverallMultiplier = 0.95;
|
|
1287
|
+
DroidRhythmEvaluator.rhythmRatioMultiplier = 12;
|
|
1294
1288
|
|
|
1295
1289
|
/**
|
|
1296
1290
|
* Represents the skill required to properly follow a beatmap's rhythm.
|
|
@@ -1492,9 +1486,8 @@ class DroidDifficultyHitObject extends DifficultyHitObject {
|
|
|
1492
1486
|
* @param difficultyHitObjects All difficulty hitobjects in the processed beatmap.
|
|
1493
1487
|
* @param clockRate The clock rate of the beatmap.
|
|
1494
1488
|
* @param greatWindow The great window of the hitobject.
|
|
1495
|
-
* @param isForceAR Whether force AR is enabled.
|
|
1496
1489
|
*/
|
|
1497
|
-
constructor(object, lastObject, lastLastObject, difficultyHitObjects, clockRate, greatWindow
|
|
1490
|
+
constructor(object, lastObject, lastLastObject, difficultyHitObjects, clockRate, greatWindow) {
|
|
1498
1491
|
super(object, lastObject, lastLastObject, difficultyHitObjects, clockRate, greatWindow);
|
|
1499
1492
|
/**
|
|
1500
1493
|
* The tap strain generated by the hitobject.
|
|
@@ -1538,10 +1531,7 @@ class DroidDifficultyHitObject extends DifficultyHitObject {
|
|
|
1538
1531
|
this.radiusBuffThreshold = 70;
|
|
1539
1532
|
this.mode = osuBase.Modes.droid;
|
|
1540
1533
|
this.maximumSliderRadius = this.normalizedRadius * 2;
|
|
1541
|
-
this.timePreempt = object.timePreempt;
|
|
1542
|
-
if (!isForceAR) {
|
|
1543
|
-
this.timePreempt /= clockRate;
|
|
1544
|
-
}
|
|
1534
|
+
this.timePreempt = object.timePreempt / clockRate;
|
|
1545
1535
|
}
|
|
1546
1536
|
computeProperties(clockRate, hitObjects) {
|
|
1547
1537
|
super.computeProperties(clockRate, hitObjects);
|
|
@@ -1553,6 +1543,9 @@ class DroidDifficultyHitObject extends DifficultyHitObject {
|
|
|
1553
1543
|
* Keep in mind that "overlapping" in this case is overlapping to the point where both hitobjects
|
|
1554
1544
|
* can be hit with just a single tap in osu!droid.
|
|
1555
1545
|
*
|
|
1546
|
+
* In the case of sliders, it is considered overlapping if all nested hitobjects can be hit with
|
|
1547
|
+
* one aim motion.
|
|
1548
|
+
*
|
|
1556
1549
|
* @param considerDistance Whether to consider the distance between both hitobjects.
|
|
1557
1550
|
* @returns Whether the hitobject is considered overlapping.
|
|
1558
1551
|
*/
|
|
@@ -1560,23 +1553,48 @@ class DroidDifficultyHitObject extends DifficultyHitObject {
|
|
|
1560
1553
|
if (this.object instanceof osuBase.Spinner) {
|
|
1561
1554
|
return false;
|
|
1562
1555
|
}
|
|
1563
|
-
const
|
|
1564
|
-
if (!
|
|
1556
|
+
const prev = this.previous(0);
|
|
1557
|
+
if (!prev || prev.object instanceof osuBase.Spinner) {
|
|
1565
1558
|
return false;
|
|
1566
1559
|
}
|
|
1567
|
-
if (this.
|
|
1560
|
+
if (this.object.startTime !== prev.object.startTime) {
|
|
1568
1561
|
return false;
|
|
1569
1562
|
}
|
|
1570
|
-
if (considerDistance) {
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1563
|
+
if (!considerDistance) {
|
|
1564
|
+
return true;
|
|
1565
|
+
}
|
|
1566
|
+
const distanceThreshold = 2 * this.object.radius;
|
|
1567
|
+
const startPosition = this.object.getStackedPosition(osuBase.Modes.droid);
|
|
1568
|
+
const prevStartPosition = prev.object.getStackedPosition(osuBase.Modes.droid);
|
|
1569
|
+
// We need to consider two cases:
|
|
1570
|
+
//
|
|
1571
|
+
// Case 1: Current object is a circle, or previous object is a circle.
|
|
1572
|
+
// In this case, we only need to check if their positions are close enough to be tapped together.
|
|
1573
|
+
//
|
|
1574
|
+
// Case 2: Both objects are sliders.
|
|
1575
|
+
// In this case, we need to check if all nested hitobjects can be hit together.
|
|
1576
|
+
// To start with, check if the starting positions can be tapped together.
|
|
1577
|
+
if (startPosition.getDistance(prevStartPosition) > distanceThreshold) {
|
|
1578
|
+
return false;
|
|
1579
|
+
}
|
|
1580
|
+
if (this.object instanceof osuBase.Circle || prev.object instanceof osuBase.Circle) {
|
|
1581
|
+
return true;
|
|
1582
|
+
}
|
|
1583
|
+
// Check if all nested hitobjects can be hit together.
|
|
1584
|
+
for (let i = 1; i < this.object.nestedHitObjects.length; ++i) {
|
|
1585
|
+
const position = this.object.nestedHitObjects[i].getStackedPosition(osuBase.Modes.droid);
|
|
1586
|
+
const prevPosition = prevStartPosition.add(prev.object.curvePositionAt(i / (this.object.nestedHitObjects.length - 1)));
|
|
1587
|
+
if (position.getDistance(prevPosition) > distanceThreshold) {
|
|
1588
|
+
return false;
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
// Do the same for the previous slider as well.
|
|
1592
|
+
for (let i = 1; i < prev.object.nestedHitObjects.length; ++i) {
|
|
1593
|
+
const prevPosition = prev.object.nestedHitObjects[i].getStackedPosition(osuBase.Modes.droid);
|
|
1594
|
+
const position = startPosition.add(this.object.curvePositionAt(i / (prev.object.nestedHitObjects.length - 1)));
|
|
1595
|
+
if (prevPosition.getDistance(position) > distanceThreshold) {
|
|
1596
|
+
return false;
|
|
1578
1597
|
}
|
|
1579
|
-
return distance <= 2 * this.object.radius;
|
|
1580
1598
|
}
|
|
1581
1599
|
return true;
|
|
1582
1600
|
}
|
|
@@ -1660,7 +1678,6 @@ class DroidDifficultyCalculator extends DifficultyCalculator {
|
|
|
1660
1678
|
speedNoteCount: 0,
|
|
1661
1679
|
sliderFactor: 0,
|
|
1662
1680
|
clockRate: 1,
|
|
1663
|
-
approachRate: 0,
|
|
1664
1681
|
overallDifficulty: 0,
|
|
1665
1682
|
hitCircleCount: 0,
|
|
1666
1683
|
sliderCount: 0,
|
|
@@ -1710,30 +1727,7 @@ class DroidDifficultyCalculator extends DifficultyCalculator {
|
|
|
1710
1727
|
return this.attributes.visualDifficulty;
|
|
1711
1728
|
}
|
|
1712
1729
|
get cacheableAttributes() {
|
|
1713
|
-
return {
|
|
1714
|
-
tapDifficulty: this.tap,
|
|
1715
|
-
rhythmDifficulty: this.rhythm,
|
|
1716
|
-
visualDifficulty: this.visual,
|
|
1717
|
-
mods: osuBase.ModUtil.modsToOsuString(this.attributes.mods),
|
|
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
|
-
};
|
|
1730
|
+
return Object.assign(Object.assign({}, this.attributes), { mods: osuBase.ModUtil.modsToOsuString(this.attributes.mods) });
|
|
1737
1731
|
}
|
|
1738
1732
|
/**
|
|
1739
1733
|
* Calculates the aim star rating of the beatmap and stores it in this instance.
|
|
@@ -1842,31 +1836,19 @@ class DroidDifficultyCalculator extends DifficultyCalculator {
|
|
|
1842
1836
|
this.visual.toFixed(2) +
|
|
1843
1837
|
" visual)");
|
|
1844
1838
|
}
|
|
1845
|
-
generateDifficultyHitObjects(
|
|
1839
|
+
generateDifficultyHitObjects(beatmap, clockRate) {
|
|
1846
1840
|
var _a, _b;
|
|
1847
1841
|
const difficultyObjects = [];
|
|
1848
|
-
const { objects } =
|
|
1849
|
-
const
|
|
1850
|
-
const greatWindow = new osuBase.
|
|
1842
|
+
const { objects } = beatmap.hitObjects;
|
|
1843
|
+
const isPrecise = this.mods.some((m) => m instanceof osuBase.ModPrecise);
|
|
1844
|
+
const greatWindow = new osuBase.DroidHitWindow(beatmap.difficulty.od).hitWindowFor300(isPrecise) / clockRate;
|
|
1851
1845
|
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(
|
|
1846
|
+
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);
|
|
1847
|
+
difficultyObject.computeProperties(clockRate, objects);
|
|
1854
1848
|
difficultyObjects.push(difficultyObject);
|
|
1855
1849
|
}
|
|
1856
1850
|
return difficultyObjects;
|
|
1857
1851
|
}
|
|
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
1852
|
createSkills() {
|
|
1871
1853
|
return [
|
|
1872
1854
|
new DroidAim(this.mods, true),
|
|
@@ -3030,7 +3012,7 @@ OsuSpeedEvaluator.SINGLE_SPACING_THRESHOLD = 125;
|
|
|
3030
3012
|
/**
|
|
3031
3013
|
* An evaluator for calculating osu!standard Rhythm skill.
|
|
3032
3014
|
*/
|
|
3033
|
-
class OsuRhythmEvaluator
|
|
3015
|
+
class OsuRhythmEvaluator {
|
|
3034
3016
|
/**
|
|
3035
3017
|
* Calculates a rhythm multiplier for the difficulty of the tap associated
|
|
3036
3018
|
* with historic data of the current object.
|
|
@@ -3136,6 +3118,8 @@ class OsuRhythmEvaluator extends RhythmEvaluator {
|
|
|
3136
3118
|
return Math.sqrt(4 + rhythmComplexitySum * this.rhythmMultiplier) / 2;
|
|
3137
3119
|
}
|
|
3138
3120
|
}
|
|
3121
|
+
OsuRhythmEvaluator.rhythmMultiplier = 0.75;
|
|
3122
|
+
OsuRhythmEvaluator.historyTimeMax = 5000; // 5 seconds of calculateRhythmBonus max.
|
|
3139
3123
|
|
|
3140
3124
|
/**
|
|
3141
3125
|
* 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 +3431,19 @@ class OsuDifficultyCalculator extends DifficultyCalculator {
|
|
|
3447
3431
|
this.flashlight.toFixed(2) +
|
|
3448
3432
|
" flashlight)");
|
|
3449
3433
|
}
|
|
3450
|
-
generateDifficultyHitObjects(
|
|
3434
|
+
generateDifficultyHitObjects(beatmap, clockRate) {
|
|
3451
3435
|
var _a, _b;
|
|
3452
3436
|
const difficultyObjects = [];
|
|
3453
|
-
const { objects } =
|
|
3454
|
-
const greatWindow = new osuBase.OsuHitWindow(
|
|
3437
|
+
const { objects } = beatmap.hitObjects;
|
|
3438
|
+
const greatWindow = new osuBase.OsuHitWindow(beatmap.difficulty.od).hitWindowFor300() /
|
|
3439
|
+
clockRate;
|
|
3455
3440
|
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(
|
|
3441
|
+
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);
|
|
3442
|
+
difficultyObject.computeProperties(clockRate, objects);
|
|
3458
3443
|
difficultyObjects.push(difficultyObject);
|
|
3459
3444
|
}
|
|
3460
3445
|
return difficultyObjects;
|
|
3461
3446
|
}
|
|
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
3447
|
createSkills() {
|
|
3474
3448
|
return [
|
|
3475
3449
|
new OsuAim(this.mods, true),
|
|
@@ -3478,6 +3452,11 @@ class OsuDifficultyCalculator extends DifficultyCalculator {
|
|
|
3478
3452
|
new OsuFlashlight(this.mods),
|
|
3479
3453
|
];
|
|
3480
3454
|
}
|
|
3455
|
+
populateDifficultyAttributes(beatmap, clockRate) {
|
|
3456
|
+
super.populateDifficultyAttributes(beatmap, clockRate);
|
|
3457
|
+
const preempt = osuBase.BeatmapDifficulty.difficultyRange(beatmap.difficulty.ar, osuBase.HitObject.preemptMax, osuBase.HitObject.preemptMid, osuBase.HitObject.preemptMin) / clockRate;
|
|
3458
|
+
this.attributes.approachRate = osuBase.BeatmapDifficulty.inverseDifficultyRange(preempt, osuBase.HitObject.preemptMax, osuBase.HitObject.preemptMid, osuBase.HitObject.preemptMin);
|
|
3459
|
+
}
|
|
3481
3460
|
/**
|
|
3482
3461
|
* Called after aim skill calculation.
|
|
3483
3462
|
*
|
|
@@ -3784,6 +3763,5 @@ exports.OsuRhythmEvaluator = OsuRhythmEvaluator;
|
|
|
3784
3763
|
exports.OsuSpeed = OsuSpeed;
|
|
3785
3764
|
exports.OsuSpeedEvaluator = OsuSpeedEvaluator;
|
|
3786
3765
|
exports.PerformanceCalculator = PerformanceCalculator;
|
|
3787
|
-
exports.RhythmEvaluator = RhythmEvaluator;
|
|
3788
3766
|
exports.SpeedEvaluator = SpeedEvaluator;
|
|
3789
3767
|
//# 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.26",
|
|
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.26"
|
|
37
37
|
},
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "1c353ee9455d80a5ca8358762f107899d64f1d5e"
|
|
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
|
*
|
|
@@ -413,8 +401,11 @@ declare abstract class DifficultyCalculator<THitObject extends DifficultyHitObje
|
|
|
413
401
|
protected abstract createSkills(): Skill[];
|
|
414
402
|
/**
|
|
415
403
|
* Populates the stored difficulty attributes with necessary data.
|
|
404
|
+
*
|
|
405
|
+
* @param beatmap The beatmap to populate the attributes with.
|
|
406
|
+
* @param clockRate The clock rate of the beatmap.
|
|
416
407
|
*/
|
|
417
|
-
protected populateDifficultyAttributes(): void;
|
|
408
|
+
protected populateDifficultyAttributes(beatmap: Beatmap, clockRate: number): void;
|
|
418
409
|
/**
|
|
419
410
|
* Calculates the star rating value of a difficulty.
|
|
420
411
|
*
|
|
@@ -616,9 +607,8 @@ declare class DroidDifficultyHitObject extends DifficultyHitObject {
|
|
|
616
607
|
* @param difficultyHitObjects All difficulty hitobjects in the processed beatmap.
|
|
617
608
|
* @param clockRate The clock rate of the beatmap.
|
|
618
609
|
* @param greatWindow The great window of the hitobject.
|
|
619
|
-
* @param isForceAR Whether force AR is enabled.
|
|
620
610
|
*/
|
|
621
|
-
constructor(object: PlaceableHitObject, lastObject: PlaceableHitObject | null, lastLastObject: PlaceableHitObject | null, difficultyHitObjects: readonly DifficultyHitObject[], clockRate: number, greatWindow: number
|
|
611
|
+
constructor(object: PlaceableHitObject, lastObject: PlaceableHitObject | null, lastLastObject: PlaceableHitObject | null, difficultyHitObjects: readonly DifficultyHitObject[], clockRate: number, greatWindow: number);
|
|
622
612
|
computeProperties(clockRate: number, hitObjects: readonly PlaceableHitObject[]): void;
|
|
623
613
|
/**
|
|
624
614
|
* Determines whether this hitobject is considered overlapping with the hitobject before it.
|
|
@@ -626,6 +616,9 @@ declare class DroidDifficultyHitObject extends DifficultyHitObject {
|
|
|
626
616
|
* Keep in mind that "overlapping" in this case is overlapping to the point where both hitobjects
|
|
627
617
|
* can be hit with just a single tap in osu!droid.
|
|
628
618
|
*
|
|
619
|
+
* In the case of sliders, it is considered overlapping if all nested hitobjects can be hit with
|
|
620
|
+
* one aim motion.
|
|
621
|
+
*
|
|
629
622
|
* @param considerDistance Whether to consider the distance between both hitobjects.
|
|
630
623
|
* @returns Whether the hitobject is considered overlapping.
|
|
631
624
|
*/
|
|
@@ -858,8 +851,7 @@ declare class DroidDifficultyCalculator extends DifficultyCalculator<DroidDiffic
|
|
|
858
851
|
calculateTotal(): void;
|
|
859
852
|
calculateAll(): void;
|
|
860
853
|
toString(): string;
|
|
861
|
-
protected generateDifficultyHitObjects(
|
|
862
|
-
protected computeDifficultyStatistics(options?: DroidDifficultyCalculationOptions): DifficultyStatisticsCalculatorResult<number, number, number, number>;
|
|
854
|
+
protected generateDifficultyHitObjects(beatmap: Beatmap, clockRate: number): DroidDifficultyHitObject[];
|
|
863
855
|
protected createSkills(): DroidSkill[];
|
|
864
856
|
/**
|
|
865
857
|
* Called after aim skill calculation.
|
|
@@ -1266,24 +1258,14 @@ declare class DroidRhythm extends DroidSkill {
|
|
|
1266
1258
|
protected saveToHitObject(current: DroidDifficultyHitObject): void;
|
|
1267
1259
|
}
|
|
1268
1260
|
|
|
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
1261
|
/**
|
|
1280
1262
|
* An evaluator for calculating osu!droid Rhythm skill.
|
|
1281
1263
|
*/
|
|
1282
|
-
declare abstract class DroidRhythmEvaluator
|
|
1283
|
-
|
|
1284
|
-
protected static readonly historyTimeMax = 4000;
|
|
1285
|
-
private static readonly maxIslandSize;
|
|
1264
|
+
declare abstract class DroidRhythmEvaluator {
|
|
1265
|
+
private static readonly historyTimeMax;
|
|
1286
1266
|
private static readonly historyObjectsMax;
|
|
1267
|
+
private static readonly rhythmOverallMultiplier;
|
|
1268
|
+
private static readonly rhythmRatioMultiplier;
|
|
1287
1269
|
/**
|
|
1288
1270
|
* Calculates a rhythm multiplier for the difficulty of the tap associated
|
|
1289
1271
|
* with historic data of the current object.
|
|
@@ -1481,6 +1463,12 @@ declare abstract class OsuAimEvaluator extends AimEvaluator {
|
|
|
1481
1463
|
* Holds data that can be used to calculate osu!standard performance points.
|
|
1482
1464
|
*/
|
|
1483
1465
|
interface OsuDifficultyAttributes extends DifficultyAttributes {
|
|
1466
|
+
/**
|
|
1467
|
+
* The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
|
|
1468
|
+
*
|
|
1469
|
+
* Rate-adjusting mods don't directly affect the approach rate difficulty value, but have a perceived effect as a result of adjusting audio timing.
|
|
1470
|
+
*/
|
|
1471
|
+
approachRate: number;
|
|
1484
1472
|
/**
|
|
1485
1473
|
* The difficulty corresponding to the speed skill.
|
|
1486
1474
|
*/
|
|
@@ -1522,9 +1510,9 @@ declare class OsuDifficultyCalculator extends DifficultyCalculator<OsuDifficulty
|
|
|
1522
1510
|
calculateTotal(): void;
|
|
1523
1511
|
calculateAll(): void;
|
|
1524
1512
|
toString(): string;
|
|
1525
|
-
protected generateDifficultyHitObjects(
|
|
1526
|
-
protected computeDifficultyStatistics(options?: DifficultyCalculationOptions): DifficultyStatisticsCalculatorResult<number, number, number, number>;
|
|
1513
|
+
protected generateDifficultyHitObjects(beatmap: Beatmap, clockRate: number): OsuDifficultyHitObject[];
|
|
1527
1514
|
protected createSkills(): OsuSkill[];
|
|
1515
|
+
protected populateDifficultyAttributes(beatmap: Beatmap, clockRate: number): void;
|
|
1528
1516
|
/**
|
|
1529
1517
|
* Called after aim skill calculation.
|
|
1530
1518
|
*
|
|
@@ -1632,7 +1620,9 @@ declare class OsuPerformanceCalculator extends PerformanceCalculator<OsuDifficul
|
|
|
1632
1620
|
/**
|
|
1633
1621
|
* An evaluator for calculating osu!standard Rhythm skill.
|
|
1634
1622
|
*/
|
|
1635
|
-
declare abstract class OsuRhythmEvaluator
|
|
1623
|
+
declare abstract class OsuRhythmEvaluator {
|
|
1624
|
+
private static readonly rhythmMultiplier;
|
|
1625
|
+
private static readonly historyTimeMax;
|
|
1636
1626
|
/**
|
|
1637
1627
|
* Calculates a rhythm multiplier for the difficulty of the tap associated
|
|
1638
1628
|
* with historic data of the current object.
|
|
@@ -1686,4 +1676,4 @@ declare abstract class OsuSpeedEvaluator extends SpeedEvaluator {
|
|
|
1686
1676
|
static evaluateDifficultyOf(current: OsuDifficultyHitObject): number;
|
|
1687
1677
|
}
|
|
1688
1678
|
|
|
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,
|
|
1679
|
+
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 };
|