@rian8337/osu-droid-replay-analyzer 1.4.12-alpha.2 → 1.4.12-alpha.5
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.
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ThreeFingerChecker = void 0;
|
|
4
4
|
const osu_base_1 = require("@rian8337/osu-base");
|
|
5
|
-
const
|
|
5
|
+
const hitResult_1 = require("../constants/hitResult");
|
|
6
|
+
const movementType_1 = require("../constants/movementType");
|
|
6
7
|
const CursorData_1 = require("../data/CursorData");
|
|
7
8
|
const BeatmapSectionGenerator_1 = require("./BeatmapSectionGenerator");
|
|
8
9
|
const ThreeFingerBeatmapSection_1 = require("./data/ThreeFingerBeatmapSection");
|
|
@@ -49,7 +50,7 @@ class ThreeFingerChecker {
|
|
|
49
50
|
* Note that this value does not account for the speed multiplier of
|
|
50
51
|
* the play, similar to the way replay object data is stored.
|
|
51
52
|
*/
|
|
52
|
-
this.maxSectionDeltaTime =
|
|
53
|
+
this.maxSectionDeltaTime = 1000;
|
|
53
54
|
/**
|
|
54
55
|
* The minimum object count required to make a beatmap section.
|
|
55
56
|
*
|
|
@@ -121,7 +122,7 @@ class ThreeFingerChecker {
|
|
|
121
122
|
return { is3Finger: false, penalty: 1 };
|
|
122
123
|
}
|
|
123
124
|
this.getBeatmapSections();
|
|
124
|
-
this.
|
|
125
|
+
this.assignFingerIndexes();
|
|
125
126
|
this.getDetailedBeatmapSections();
|
|
126
127
|
this.preventAccidentalTaps();
|
|
127
128
|
if (this.downCursorInstances.filter((v) => v.size > 0).length <= 3) {
|
|
@@ -147,11 +148,11 @@ class ThreeFingerChecker {
|
|
|
147
148
|
// For sliders and spinners, automatically set hit window length to be as lenient as possible.
|
|
148
149
|
let beforeIndexHitWindowLength = this.hitWindow.hitWindowFor50(isPrecise);
|
|
149
150
|
switch (objectData[beforeIndex].result) {
|
|
150
|
-
case
|
|
151
|
+
case hitResult_1.hitResult.RESULT_300:
|
|
151
152
|
beforeIndexHitWindowLength =
|
|
152
153
|
this.hitWindow.hitWindowFor300(isPrecise);
|
|
153
154
|
break;
|
|
154
|
-
case
|
|
155
|
+
case hitResult_1.hitResult.RESULT_100:
|
|
155
156
|
beforeIndexHitWindowLength =
|
|
156
157
|
this.hitWindow.hitWindowFor100(isPrecise);
|
|
157
158
|
break;
|
|
@@ -165,11 +166,11 @@ class ThreeFingerChecker {
|
|
|
165
166
|
// For sliders and spinners, automatically set hit window length to be as lenient as possible.
|
|
166
167
|
let afterIndexHitWindowLength = this.hitWindow.hitWindowFor50(isPrecise);
|
|
167
168
|
switch (objectData[afterIndex].result) {
|
|
168
|
-
case
|
|
169
|
+
case hitResult_1.hitResult.RESULT_300:
|
|
169
170
|
afterIndexHitWindowLength =
|
|
170
171
|
this.hitWindow.hitWindowFor300(isPrecise);
|
|
171
172
|
break;
|
|
172
|
-
case
|
|
173
|
+
case hitResult_1.hitResult.RESULT_100:
|
|
173
174
|
afterIndexHitWindowLength =
|
|
174
175
|
this.hitWindow.hitWindowFor100(isPrecise);
|
|
175
176
|
break;
|
|
@@ -199,11 +200,11 @@ class ThreeFingerChecker {
|
|
|
199
200
|
let firstObjectHitWindow = this.hitWindow.hitWindowFor50(isPrecise);
|
|
200
201
|
if (objects[0].object instanceof osu_base_1.Circle) {
|
|
201
202
|
switch (firstObjectResult) {
|
|
202
|
-
case
|
|
203
|
+
case hitResult_1.hitResult.RESULT_300:
|
|
203
204
|
firstObjectHitWindow =
|
|
204
205
|
this.hitWindow.hitWindowFor300(isPrecise);
|
|
205
206
|
break;
|
|
206
|
-
case
|
|
207
|
+
case hitResult_1.hitResult.RESULT_100:
|
|
207
208
|
firstObjectHitWindow =
|
|
208
209
|
this.hitWindow.hitWindowFor100(isPrecise);
|
|
209
210
|
break;
|
|
@@ -216,11 +217,11 @@ class ThreeFingerChecker {
|
|
|
216
217
|
let lastObjectHitWindow = this.hitWindow.hitWindowFor50(isPrecise);
|
|
217
218
|
if (objects.at(-1).object instanceof osu_base_1.Circle) {
|
|
218
219
|
switch (lastObjectResult) {
|
|
219
|
-
case
|
|
220
|
+
case hitResult_1.hitResult.RESULT_300:
|
|
220
221
|
lastObjectHitWindow =
|
|
221
222
|
this.hitWindow.hitWindowFor300(isPrecise);
|
|
222
223
|
break;
|
|
223
|
-
case
|
|
224
|
+
case hitResult_1.hitResult.RESULT_100:
|
|
224
225
|
lastObjectHitWindow =
|
|
225
226
|
this.hitWindow.hitWindowFor100(isPrecise);
|
|
226
227
|
break;
|
|
@@ -244,7 +245,7 @@ class ThreeFingerChecker {
|
|
|
244
245
|
id: [],
|
|
245
246
|
});
|
|
246
247
|
for (let j = 0; j < cursorInstance.size; ++j) {
|
|
247
|
-
if (cursorInstance.id[j] !==
|
|
248
|
+
if (cursorInstance.id[j] !== movementType_1.movementType.DOWN) {
|
|
248
249
|
continue;
|
|
249
250
|
}
|
|
250
251
|
const time = cursorInstance.time[j];
|
|
@@ -273,162 +274,113 @@ class ThreeFingerChecker {
|
|
|
273
274
|
this.beatmapSections.push(new ThreeFingerBeatmapSection_1.ThreeFingerBeatmapSection({
|
|
274
275
|
firstObjectIndex: beatmapSection.firstObjectIndex,
|
|
275
276
|
lastObjectIndex: beatmapSection.lastObjectIndex,
|
|
276
|
-
|
|
277
|
-
dragFingerIndex: -1,
|
|
277
|
+
mainFingerIndex: -1,
|
|
278
278
|
}));
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
281
|
/**
|
|
282
|
-
*
|
|
282
|
+
* Assigns main finger indexes for each beatmap section.
|
|
283
283
|
*/
|
|
284
|
-
|
|
285
|
-
for (
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
284
|
+
assignFingerIndexes() {
|
|
285
|
+
for (const section of this.beatmapSections) {
|
|
286
|
+
let currentIndex = this.getMainFingerIndex(section);
|
|
287
|
+
if (currentIndex === null) {
|
|
288
|
+
const firstObject = this.map.objects[section.firstObjectIndex];
|
|
289
|
+
const firstObjectData = this.data.hitObjectData[section.firstObjectIndex];
|
|
290
|
+
const lastObject = this.map.objects[section.lastObjectIndex];
|
|
291
|
+
const lastObjectData = this.data.hitObjectData[section.lastObjectIndex];
|
|
292
|
+
const startTime = firstObject.object.startTime + firstObjectData.accuracy;
|
|
293
|
+
const endTime = lastObject.object.endTime + lastObjectData.accuracy;
|
|
294
|
+
// Get the cursor instance index in the section that has the most movementType.MOVE instance.
|
|
295
|
+
const moveInstanceAmounts = [];
|
|
296
|
+
for (let i = 0; i < this.data.cursorMovement.length; ++i) {
|
|
297
|
+
let amount = 0;
|
|
298
|
+
const c = this.data.cursorMovement[i];
|
|
299
|
+
for (let j = 0; j < c.size; ++j) {
|
|
300
|
+
if (c.time[j] < startTime) {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (c.time[j] > endTime) {
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
if (c.id[j] === movementType_1.movementType.MOVE) {
|
|
307
|
+
++amount;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
moveInstanceAmounts.push(amount);
|
|
311
|
+
}
|
|
312
|
+
currentIndex = moveInstanceAmounts.indexOf(Math.max(...moveInstanceAmounts));
|
|
313
|
+
}
|
|
314
|
+
section.mainFingerIndex = currentIndex;
|
|
289
315
|
}
|
|
290
316
|
}
|
|
291
317
|
/**
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
* If the section is not dragged, -1 will be returned.
|
|
318
|
+
* Gets the main finger index of a beatmap section.
|
|
295
319
|
*
|
|
296
|
-
* @param section The section
|
|
320
|
+
* @param section The beatmap section.
|
|
297
321
|
*/
|
|
298
|
-
|
|
299
|
-
const
|
|
300
|
-
const
|
|
301
|
-
const isPrecise = this.map.mods.some((m) => m instanceof osu_base_1.ModPrecise);
|
|
302
|
-
const firstObject = objects[section.firstObjectIndex];
|
|
303
|
-
const lastObject = objects[section.lastObjectIndex];
|
|
304
|
-
let firstObjectMinHitTime = firstObject.object.startTime;
|
|
305
|
-
if (firstObject.object instanceof osu_base_1.Circle) {
|
|
306
|
-
switch (objectData[section.firstObjectIndex].result) {
|
|
307
|
-
case __1.hitResult.RESULT_300:
|
|
308
|
-
firstObjectMinHitTime -=
|
|
309
|
-
this.hitWindow.hitWindowFor300(isPrecise);
|
|
310
|
-
break;
|
|
311
|
-
case __1.hitResult.RESULT_100:
|
|
312
|
-
firstObjectMinHitTime -=
|
|
313
|
-
this.hitWindow.hitWindowFor100(isPrecise);
|
|
314
|
-
break;
|
|
315
|
-
default:
|
|
316
|
-
firstObjectMinHitTime -=
|
|
317
|
-
this.hitWindow.hitWindowFor50(isPrecise);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
else {
|
|
321
|
-
firstObjectMinHitTime -= this.hitWindow.hitWindowFor50(isPrecise);
|
|
322
|
-
}
|
|
323
|
-
let lastObjectMaxHitTime = lastObject.object.startTime;
|
|
324
|
-
if (lastObject.object instanceof osu_base_1.Circle) {
|
|
325
|
-
switch (objectData[section.lastObjectIndex].result) {
|
|
326
|
-
case __1.hitResult.RESULT_300:
|
|
327
|
-
lastObjectMaxHitTime +=
|
|
328
|
-
this.hitWindow.hitWindowFor300(isPrecise);
|
|
329
|
-
break;
|
|
330
|
-
case __1.hitResult.RESULT_100:
|
|
331
|
-
lastObjectMaxHitTime +=
|
|
332
|
-
this.hitWindow.hitWindowFor100(isPrecise);
|
|
333
|
-
break;
|
|
334
|
-
default:
|
|
335
|
-
lastObjectMaxHitTime +=
|
|
336
|
-
this.hitWindow.hitWindowFor50(isPrecise);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
else {
|
|
340
|
-
lastObjectMaxHitTime += this.hitWindow.hitWindowFor50(isPrecise);
|
|
341
|
-
}
|
|
342
|
-
// Since there may be more than 1 cursor instance index,
|
|
343
|
-
// we check which cursor instance follows hitobjects all over.
|
|
344
|
-
const cursorIndexes = [];
|
|
322
|
+
getMainFingerIndex(section) {
|
|
323
|
+
const validCursorIndexes = new Set();
|
|
324
|
+
const timeDiscrepancy = 10;
|
|
345
325
|
for (let i = 0; i < this.data.cursorMovement.length; ++i) {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
// Do not include cursors that don't have an occurence in this section
|
|
351
|
-
// this speeds up checking process.
|
|
352
|
-
if (c.time.filter((v) => v >= firstObjectMinHitTime && v <= lastObjectMaxHitTime).length === 0) {
|
|
353
|
-
continue;
|
|
326
|
+
if (this.data.cursorMovement[i].size > 0) {
|
|
327
|
+
validCursorIndexes.add(i);
|
|
354
328
|
}
|
|
355
|
-
// If this cursor instance doesn't move, it's not the cursor instance we want.
|
|
356
|
-
if (c.id.filter((v) => v === __1.movementType.MOVE).length === 0) {
|
|
357
|
-
continue;
|
|
358
|
-
}
|
|
359
|
-
cursorIndexes.push(i);
|
|
360
|
-
}
|
|
361
|
-
return this.findDragIndex(objects.slice(section.firstObjectIndex, section.lastObjectIndex + 1), objectData.slice(section.firstObjectIndex, section.lastObjectIndex + 1), cursorIndexes);
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* Finds the drag index of the section.
|
|
365
|
-
*
|
|
366
|
-
* @param sectionObjects The objects in the section.
|
|
367
|
-
* @param sectionReplayObjectData The hitobject data of all objects in the section.
|
|
368
|
-
* @param cursorIndexes The indexes of the cursor instance that has at least an occurrence in the section.
|
|
369
|
-
*/
|
|
370
|
-
findDragIndex(sectionObjects, sectionReplayObjectData, cursorIndexes) {
|
|
371
|
-
let objectIndex = sectionObjects.findIndex((v, i) => !(v.object instanceof osu_base_1.Spinner) &&
|
|
372
|
-
sectionReplayObjectData[i].result !== __1.hitResult.RESULT_0);
|
|
373
|
-
if (objectIndex === -1) {
|
|
374
|
-
return -1;
|
|
375
329
|
}
|
|
376
|
-
|
|
377
|
-
if (
|
|
330
|
+
for (let i = section.firstObjectIndex; i <= section.lastObjectIndex; ++i) {
|
|
331
|
+
if (validCursorIndexes.size === 1) {
|
|
378
332
|
break;
|
|
379
333
|
}
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
++objectIndex;
|
|
383
|
-
if (s.result === __1.hitResult.RESULT_0) {
|
|
334
|
+
const object = this.map.objects[i];
|
|
335
|
+
if (object.object instanceof osu_base_1.Spinner) {
|
|
384
336
|
continue;
|
|
385
337
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
// therefore the game emulates the movement between
|
|
391
|
-
// movementType.MOVE cursors.
|
|
392
|
-
const hitTime = o.object.startTime + s.accuracy;
|
|
393
|
-
const nextHitIndex = c.time.findIndex((v) => v >= hitTime);
|
|
394
|
-
const hitIndex = nextHitIndex - 1;
|
|
395
|
-
if (hitIndex <= -1) {
|
|
396
|
-
cursorIndexes[j] = -1;
|
|
397
|
-
continue;
|
|
398
|
-
}
|
|
399
|
-
if (c.id[hitIndex] === __1.movementType.UP) {
|
|
400
|
-
cursorIndexes[j] = -1;
|
|
338
|
+
const objectData = this.data.hitObjectData[i];
|
|
339
|
+
const hitTime = object.startTime + objectData.accuracy;
|
|
340
|
+
for (let j = 0; j < this.data.cursorMovement.length; ++j) {
|
|
341
|
+
if (!validCursorIndexes.has(j)) {
|
|
401
342
|
continue;
|
|
402
343
|
}
|
|
403
|
-
const
|
|
404
|
-
let
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
344
|
+
const c = this.data.cursorMovement[j];
|
|
345
|
+
for (let k = 0; k < c.size; ++k) {
|
|
346
|
+
if (c.time[k] < hitTime - timeDiscrepancy ||
|
|
347
|
+
c.id[k] === movementType_1.movementType.UP) {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
if (c.time[k] > hitTime + timeDiscrepancy) {
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
// Calculate the initial cursor position assuming it's a DOWN movement type.
|
|
354
|
+
const cursorPosition = new osu_base_1.Vector2(c.x[k], c.y[k]);
|
|
355
|
+
let isInObject = false;
|
|
356
|
+
// But if the next cursor is a move instance, then we extend through
|
|
357
|
+
// time between both cursors and interpolate cursor position.
|
|
358
|
+
if (c.id[k + 1] === movementType_1.movementType.MOVE) {
|
|
359
|
+
// Loop every 1ms to increase accuracy and minimalize error.
|
|
360
|
+
for (let mSecPassed = c.time[k]; mSecPassed <= c.time[k + 1]; ++mSecPassed) {
|
|
361
|
+
const t = (mSecPassed - c.time[k + 1]) /
|
|
362
|
+
(c.time[k] - c.time[k + 1]);
|
|
363
|
+
cursorPosition.x = osu_base_1.Interpolation.lerp(c.x[k], c.x[k + 1], t);
|
|
364
|
+
cursorPosition.y = osu_base_1.Interpolation.lerp(c.y[k], c.y[k + 1], t);
|
|
365
|
+
if (object.object.stackedPosition.getDistance(cursorPosition) <= object.object.radius) {
|
|
366
|
+
isInObject = true;
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
417
369
|
}
|
|
418
370
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
371
|
+
else {
|
|
372
|
+
isInObject =
|
|
373
|
+
object.object.stackedPosition.getDistance(cursorPosition) <= object.object.radius;
|
|
374
|
+
}
|
|
375
|
+
if (!isInObject) {
|
|
376
|
+
validCursorIndexes.delete(j);
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
427
379
|
}
|
|
428
380
|
}
|
|
429
|
-
cursorIndexes = cursorIndexes.filter((v) => v !== -1);
|
|
430
381
|
}
|
|
431
|
-
|
|
382
|
+
const [first] = validCursorIndexes;
|
|
383
|
+
return first ?? null;
|
|
432
384
|
}
|
|
433
385
|
/**
|
|
434
386
|
* Redivides the beatmap into sections.
|
|
@@ -457,18 +409,15 @@ class ThreeFingerChecker {
|
|
|
457
409
|
newBeatmapSections.push({
|
|
458
410
|
firstObjectIndex: newFirstObjectIndex,
|
|
459
411
|
lastObjectIndex: i,
|
|
460
|
-
|
|
461
|
-
dragFingerIndex: beatmapSection.dragFingerIndex,
|
|
412
|
+
mainFingerIndex: beatmapSection.mainFingerIndex,
|
|
462
413
|
});
|
|
463
414
|
}
|
|
464
415
|
}
|
|
465
416
|
// Don't forget to manually add the last beatmap section, which would otherwise be ignored.
|
|
466
417
|
if (inSpeedSection) {
|
|
467
418
|
newBeatmapSections.push({
|
|
419
|
+
...beatmapSection,
|
|
468
420
|
firstObjectIndex: newFirstObjectIndex,
|
|
469
|
-
lastObjectIndex: beatmapSection.lastObjectIndex,
|
|
470
|
-
isDragged: beatmapSection.isDragged,
|
|
471
|
-
dragFingerIndex: beatmapSection.dragFingerIndex,
|
|
472
421
|
});
|
|
473
422
|
}
|
|
474
423
|
}
|
|
@@ -525,15 +474,15 @@ class ThreeFingerChecker {
|
|
|
525
474
|
// We only filter cursor instances that are above the strain threshold.
|
|
526
475
|
// This minimalizes the amount of cursor instances to analyze.
|
|
527
476
|
for (const beatmapSection of this.beatmapSections) {
|
|
528
|
-
const
|
|
477
|
+
const mainIndex = beatmapSection.mainFingerIndex;
|
|
529
478
|
const startTime = objects[beatmapSection.firstObjectIndex].object.startTime +
|
|
530
479
|
(objectData[beatmapSection.firstObjectIndex].result !==
|
|
531
|
-
|
|
480
|
+
hitResult_1.hitResult.RESULT_0
|
|
532
481
|
? objectData[beatmapSection.firstObjectIndex].accuracy
|
|
533
482
|
: -this.hitWindow.hitWindowFor50(isPrecise));
|
|
534
483
|
const endTime = objects[beatmapSection.lastObjectIndex].object.endTime +
|
|
535
484
|
(objectData[beatmapSection.lastObjectIndex].result !==
|
|
536
|
-
|
|
485
|
+
hitResult_1.hitResult.RESULT_0
|
|
537
486
|
? objectData[beatmapSection.lastObjectIndex].accuracy
|
|
538
487
|
: this.hitWindow.hitWindowFor50(isPrecise));
|
|
539
488
|
// Filter cursor instances during section.
|
|
@@ -550,10 +499,6 @@ class ThreeFingerChecker {
|
|
|
550
499
|
const cursorAmounts = [];
|
|
551
500
|
const cursorVectorTimes = [];
|
|
552
501
|
for (let i = 0; i < this.downCursorInstances.length; ++i) {
|
|
553
|
-
// Do not include drag cursor instance.
|
|
554
|
-
if (i === dragIndex) {
|
|
555
|
-
continue;
|
|
556
|
-
}
|
|
557
502
|
const cursorData = this.downCursorInstances[i];
|
|
558
503
|
let amount = 0;
|
|
559
504
|
for (let j = 0; j < cursorData.size; ++j) {
|
|
@@ -568,18 +513,25 @@ class ThreeFingerChecker {
|
|
|
568
513
|
}
|
|
569
514
|
cursorAmounts.push(amount);
|
|
570
515
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
516
|
+
let validCursorPressCount = 0;
|
|
517
|
+
let invalidCursorPressCount = 0;
|
|
518
|
+
const validIndexes = [];
|
|
519
|
+
for (let i = 0; i < cursorAmounts.length; ++i) {
|
|
520
|
+
// Ignore the main cursor index.
|
|
521
|
+
if (i === mainIndex) {
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
// Add 2f cursor presses to valid cursor count and the rest to invalid cursor count.
|
|
525
|
+
if (validIndexes.length <= 2) {
|
|
526
|
+
validCursorPressCount += cursorAmounts[i];
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
invalidCursorPressCount += cursorAmounts[i];
|
|
530
|
+
}
|
|
531
|
+
validIndexes.push(i);
|
|
532
|
+
}
|
|
533
|
+
validIndexes.push(mainIndex);
|
|
534
|
+
const threeFingerRatio = invalidCursorPressCount / validCursorPressCount;
|
|
583
535
|
const similarPresses = [];
|
|
584
536
|
for (const cursorVectorTime of cursorVectorTimes) {
|
|
585
537
|
const pressIndex = similarPresses.findIndex((v) => v.vector.getDistance(cursorVectorTime.vector) <=
|
|
@@ -627,25 +579,29 @@ class ThreeFingerChecker {
|
|
|
627
579
|
.slice(beatmapSection.firstObjectIndex, beatmapSection.lastObjectIndex)
|
|
628
580
|
.reduce((acc, value) => acc +
|
|
629
581
|
value.originalTapStrain /
|
|
630
|
-
ThreeFingerChecker.strainThreshold, 0), 0.
|
|
631
|
-
// We can ignore the first 3 (2 for drag) filled cursor instances
|
|
632
|
-
// since they are guaranteed not 3 finger.
|
|
633
|
-
const threeFingerCursorAmounts = cursorAmounts
|
|
634
|
-
.slice(fingerSplitIndex)
|
|
635
|
-
.filter((amount) => amount > 0);
|
|
582
|
+
ThreeFingerChecker.strainThreshold, 0), 0.875);
|
|
636
583
|
// Finger factor applies more penalty if more fingers were used.
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
584
|
+
let fingerFactor = 1;
|
|
585
|
+
if (threeFingerRatio > this.threeFingerRatioThreshold) {
|
|
586
|
+
for (let i = 0, penaltyIndex = 1; i < cursorAmounts.length; ++i) {
|
|
587
|
+
if (validIndexes.includes(i)) {
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
fingerFactor += Math.pow((penaltyIndex * cursorAmounts[i] * objectCount) /
|
|
591
|
+
this.strainNoteCount, 0.8);
|
|
592
|
+
++penaltyIndex;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
fingerFactor = Math.pow(validPresses.reduce((acc, value, index) => acc +
|
|
642
597
|
Math.pow(((index + 1) *
|
|
643
598
|
(value.count /
|
|
644
599
|
(this
|
|
645
600
|
.cursorDistancingCountThreshold *
|
|
646
601
|
2)) *
|
|
647
602
|
objectCount) /
|
|
648
|
-
this.strainNoteCount, 0.2), 1), 0.
|
|
603
|
+
this.strainNoteCount, 0.2), 1), 0.25);
|
|
604
|
+
}
|
|
649
605
|
// Length factor applies more penalty if there are more 3-fingered object.
|
|
650
606
|
const lengthFactor = 1 + Math.pow(objectCount / this.strainNoteCount, 1.2);
|
|
651
607
|
this.nerfFactors.push({
|
|
@@ -3,13 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ThreeFingerBeatmapSection = void 0;
|
|
4
4
|
const BeatmapSection_1 = require("./BeatmapSection");
|
|
5
5
|
/**
|
|
6
|
-
* A section of a beatmap
|
|
6
|
+
* A section of a beatmap with extra information used for detecting three-finger usage.
|
|
7
7
|
*/
|
|
8
8
|
class ThreeFingerBeatmapSection extends BeatmapSection_1.BeatmapSection {
|
|
9
9
|
constructor(values) {
|
|
10
10
|
super(values.firstObjectIndex, values.lastObjectIndex);
|
|
11
|
-
this.
|
|
12
|
-
this.dragFingerIndex = values.dragFingerIndex;
|
|
11
|
+
this.mainFingerIndex = values.mainFingerIndex;
|
|
13
12
|
}
|
|
14
13
|
}
|
|
15
14
|
exports.ThreeFingerBeatmapSection = ThreeFingerBeatmapSection;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rian8337/osu-droid-replay-analyzer",
|
|
3
|
-
"version": "1.4.12-alpha.
|
|
3
|
+
"version": "1.4.12-alpha.5",
|
|
4
4
|
"description": "A replay analyzer for analyzing osu!droid replay files.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"osu",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@rian8337/osu-base": "^1.4.8",
|
|
35
35
|
"@rian8337/osu-difficulty-calculator": "^1.4.8",
|
|
36
|
-
"@rian8337/osu-rebalance-difficulty-calculator": "^1.4.12-alpha.
|
|
36
|
+
"@rian8337/osu-rebalance-difficulty-calculator": "^1.4.12-alpha.4",
|
|
37
37
|
"java-deserialization": "^0.1.0",
|
|
38
38
|
"unzipper": "^0.10.11"
|
|
39
39
|
},
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"publishConfig": {
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "9e42f2db1fa53115f7dcca1a6f09802267c3fe29"
|
|
49
49
|
}
|