@rian8337/osu-difficulty-calculator 3.0.0-beta.19 → 3.0.0-beta.20
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/README.md +5 -3
- package/dist/index.js +190 -131
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/typings/index.d.ts +112 -12
package/dist/index.js
CHANGED
|
@@ -573,13 +573,6 @@ class DifficultyCalculator {
|
|
|
573
573
|
speed: [],
|
|
574
574
|
flashlight: [],
|
|
575
575
|
};
|
|
576
|
-
/**
|
|
577
|
-
* Additional data that is used in performance calculation.
|
|
578
|
-
*/
|
|
579
|
-
attributes = {
|
|
580
|
-
speedNoteCount: 0,
|
|
581
|
-
sliderFactor: 1,
|
|
582
|
-
};
|
|
583
576
|
sectionLength = 400;
|
|
584
577
|
/**
|
|
585
578
|
* Constructs a new instance of the calculator.
|
|
@@ -615,6 +608,7 @@ class DifficultyCalculator {
|
|
|
615
608
|
speedMultiplier: options?.stats?.speedMultiplier,
|
|
616
609
|
oldStatistics: options?.stats?.oldStatistics,
|
|
617
610
|
}).calculate({ mode: this.mode });
|
|
611
|
+
this.populateDifficultyAttributes();
|
|
618
612
|
this.generateDifficultyHitObjects();
|
|
619
613
|
this.calculateAll();
|
|
620
614
|
return this;
|
|
@@ -650,6 +644,18 @@ class DifficultyCalculator {
|
|
|
650
644
|
});
|
|
651
645
|
});
|
|
652
646
|
}
|
|
647
|
+
/**
|
|
648
|
+
* Populates the stored difficulty attributes with necessary data.
|
|
649
|
+
*/
|
|
650
|
+
populateDifficultyAttributes() {
|
|
651
|
+
this.attributes.approachRate = this.stats.ar;
|
|
652
|
+
this.attributes.hitCircleCount = this.beatmap.hitObjects.circles;
|
|
653
|
+
this.attributes.maxCombo = this.beatmap.maxCombo;
|
|
654
|
+
this.attributes.mods = this.mods.slice();
|
|
655
|
+
this.attributes.overallDifficulty = this.stats.od;
|
|
656
|
+
this.attributes.sliderCount = this.beatmap.hitObjects.sliders;
|
|
657
|
+
this.attributes.spinnerCount = this.beatmap.hitObjects.spinners;
|
|
658
|
+
}
|
|
653
659
|
/**
|
|
654
660
|
* Calculates the star rating value of a difficulty.
|
|
655
661
|
*
|
|
@@ -1484,6 +1490,23 @@ class DroidDifficultyCalculator extends DifficultyCalculator {
|
|
|
1484
1490
|
visual = 0;
|
|
1485
1491
|
difficultyMultiplier = 0.18;
|
|
1486
1492
|
mode = osuBase.Modes.droid;
|
|
1493
|
+
attributes = {
|
|
1494
|
+
tapDifficulty: 0,
|
|
1495
|
+
rhythmDifficulty: 0,
|
|
1496
|
+
visualDifficulty: 0,
|
|
1497
|
+
mods: [],
|
|
1498
|
+
starRating: 0,
|
|
1499
|
+
maxCombo: 0,
|
|
1500
|
+
aimDifficulty: 0,
|
|
1501
|
+
flashlightDifficulty: 0,
|
|
1502
|
+
speedNoteCount: 0,
|
|
1503
|
+
sliderFactor: 0,
|
|
1504
|
+
approachRate: 0,
|
|
1505
|
+
overallDifficulty: 0,
|
|
1506
|
+
hitCircleCount: 0,
|
|
1507
|
+
sliderCount: 0,
|
|
1508
|
+
spinnerCount: 0,
|
|
1509
|
+
};
|
|
1487
1510
|
/**
|
|
1488
1511
|
* Calculates the aim star rating of the beatmap and stores it in this instance.
|
|
1489
1512
|
*/
|
|
@@ -1640,6 +1663,7 @@ class DroidDifficultyCalculator extends DifficultyCalculator {
|
|
|
1640
1663
|
if (this.mods.some((m) => m instanceof osuBase.ModRelax)) {
|
|
1641
1664
|
this.aim *= 0.9;
|
|
1642
1665
|
}
|
|
1666
|
+
this.attributes.aimDifficulty = this.aim;
|
|
1643
1667
|
}
|
|
1644
1668
|
/**
|
|
1645
1669
|
* Called after tap skill calculation.
|
|
@@ -1648,7 +1672,7 @@ class DroidDifficultyCalculator extends DifficultyCalculator {
|
|
|
1648
1672
|
*/
|
|
1649
1673
|
postCalculateTap(tapSkill) {
|
|
1650
1674
|
this.strainPeaks.speed = tapSkill.strainPeaks;
|
|
1651
|
-
this.tap = this.starValue(tapSkill.difficultyValue());
|
|
1675
|
+
this.tap = this.attributes.tapDifficulty = this.starValue(tapSkill.difficultyValue());
|
|
1652
1676
|
}
|
|
1653
1677
|
/**
|
|
1654
1678
|
* Calculates speed-related attributes.
|
|
@@ -1666,7 +1690,7 @@ class DroidDifficultyCalculator extends DifficultyCalculator {
|
|
|
1666
1690
|
* @param rhythmSkill The rhythm skill.
|
|
1667
1691
|
*/
|
|
1668
1692
|
postCalculateRhythm(rhythmSkill) {
|
|
1669
|
-
this.rhythm = this.mods.some((m) => m instanceof osuBase.ModRelax)
|
|
1693
|
+
this.rhythm = this.attributes.rhythmDifficulty = this.mods.some((m) => m instanceof osuBase.ModRelax)
|
|
1670
1694
|
? 0
|
|
1671
1695
|
: this.starValue(rhythmSkill.difficultyValue());
|
|
1672
1696
|
}
|
|
@@ -1681,6 +1705,7 @@ class DroidDifficultyCalculator extends DifficultyCalculator {
|
|
|
1681
1705
|
if (this.mods.some((m) => m instanceof osuBase.ModRelax)) {
|
|
1682
1706
|
this.flashlight *= 0.7;
|
|
1683
1707
|
}
|
|
1708
|
+
this.attributes.flashlightDifficulty = this.flashlight;
|
|
1684
1709
|
}
|
|
1685
1710
|
/**
|
|
1686
1711
|
* Called after visual skill calculation.
|
|
@@ -1688,7 +1713,7 @@ class DroidDifficultyCalculator extends DifficultyCalculator {
|
|
|
1688
1713
|
* @param visualSkill The visual skill.
|
|
1689
1714
|
*/
|
|
1690
1715
|
postCalculateVisual(visualSkill) {
|
|
1691
|
-
this.visual = this.mods.some((m) => m instanceof osuBase.ModRelax)
|
|
1716
|
+
this.visual = this.attributes.visualDifficulty = this.mods.some((m) => m instanceof osuBase.ModRelax)
|
|
1692
1717
|
? 0
|
|
1693
1718
|
: this.starValue(visualSkill.difficultyValue());
|
|
1694
1719
|
}
|
|
@@ -1706,10 +1731,6 @@ class PerformanceCalculator {
|
|
|
1706
1731
|
* The calculated accuracy.
|
|
1707
1732
|
*/
|
|
1708
1733
|
computedAccuracy = new osuBase.Accuracy({});
|
|
1709
|
-
/**
|
|
1710
|
-
* The difficulty calculator that is being calculated.
|
|
1711
|
-
*/
|
|
1712
|
-
difficultyCalculator;
|
|
1713
1734
|
/**
|
|
1714
1735
|
* Penalty for combo breaks.
|
|
1715
1736
|
*/
|
|
@@ -1722,12 +1743,6 @@ class PerformanceCalculator {
|
|
|
1722
1743
|
* Nerf factor used for nerfing beatmaps with very likely dropped sliderends.
|
|
1723
1744
|
*/
|
|
1724
1745
|
sliderNerfFactor = 1;
|
|
1725
|
-
/**
|
|
1726
|
-
* @param difficultyCalculator The difficulty calculator to calculate.
|
|
1727
|
-
*/
|
|
1728
|
-
constructor(difficultyCalculator) {
|
|
1729
|
-
this.difficultyCalculator = difficultyCalculator;
|
|
1730
|
-
}
|
|
1731
1746
|
/**
|
|
1732
1747
|
* Calculates the performance points of the beatmap.
|
|
1733
1748
|
*
|
|
@@ -1740,6 +1755,14 @@ class PerformanceCalculator {
|
|
|
1740
1755
|
this.calculateTotalValue();
|
|
1741
1756
|
return this;
|
|
1742
1757
|
}
|
|
1758
|
+
/**
|
|
1759
|
+
* The total hits that can be done in the beatmap.
|
|
1760
|
+
*/
|
|
1761
|
+
get totalHits() {
|
|
1762
|
+
return (this.difficultyAttributes.hitCircleCount +
|
|
1763
|
+
this.difficultyAttributes.sliderCount +
|
|
1764
|
+
this.difficultyAttributes.spinnerCount);
|
|
1765
|
+
}
|
|
1743
1766
|
/**
|
|
1744
1767
|
* Calculates the base performance value of a star rating.
|
|
1745
1768
|
*/
|
|
@@ -1752,7 +1775,7 @@ class PerformanceCalculator {
|
|
|
1752
1775
|
* @param options Options for performance calculation.
|
|
1753
1776
|
*/
|
|
1754
1777
|
handleOptions(options) {
|
|
1755
|
-
const maxCombo = this.
|
|
1778
|
+
const maxCombo = this.difficultyAttributes.maxCombo;
|
|
1756
1779
|
const miss = this.computedAccuracy.nmiss;
|
|
1757
1780
|
const combo = options?.combo ?? maxCombo - miss;
|
|
1758
1781
|
this.comboPenalty = Math.min(Math.pow(combo / maxCombo, 0.8), 1);
|
|
@@ -1761,7 +1784,7 @@ class PerformanceCalculator {
|
|
|
1761
1784
|
this.computedAccuracy = new osuBase.Accuracy(options.accPercent);
|
|
1762
1785
|
if (this.computedAccuracy.n300 <= 0) {
|
|
1763
1786
|
this.computedAccuracy.n300 =
|
|
1764
|
-
this.
|
|
1787
|
+
this.totalHits -
|
|
1765
1788
|
this.computedAccuracy.n100 -
|
|
1766
1789
|
this.computedAccuracy.n50 -
|
|
1767
1790
|
this.computedAccuracy.nmiss;
|
|
@@ -1770,49 +1793,50 @@ class PerformanceCalculator {
|
|
|
1770
1793
|
else {
|
|
1771
1794
|
this.computedAccuracy = new osuBase.Accuracy({
|
|
1772
1795
|
percent: options?.accPercent,
|
|
1773
|
-
nobjects: this.
|
|
1796
|
+
nobjects: this.totalHits,
|
|
1774
1797
|
nmiss: options?.miss || 0,
|
|
1775
1798
|
});
|
|
1776
1799
|
}
|
|
1777
1800
|
this.effectiveMissCount = this.calculateEffectiveMissCount(combo, maxCombo);
|
|
1778
|
-
if (this.
|
|
1801
|
+
if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModNoFail)) {
|
|
1779
1802
|
this.finalMultiplier *= Math.max(0.9, 1 - 0.02 * this.effectiveMissCount);
|
|
1780
1803
|
}
|
|
1781
|
-
if (this.
|
|
1804
|
+
if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModSpunOut)) {
|
|
1782
1805
|
this.finalMultiplier *=
|
|
1783
1806
|
1 -
|
|
1784
|
-
Math.pow(this.
|
|
1785
|
-
this.difficultyCalculator.objects.length, 0.85);
|
|
1807
|
+
Math.pow(this.difficultyAttributes.spinnerCount / this.totalHits, 0.85);
|
|
1786
1808
|
}
|
|
1787
|
-
if (this.
|
|
1809
|
+
if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModRelax)) {
|
|
1788
1810
|
// Graph: https://www.desmos.com/calculator/bc9eybdthb
|
|
1789
1811
|
// We use OD13.3 as maximum since it's the value at which great hit window becomes 0.
|
|
1790
|
-
const n100Multiplier = Math.max(0, this.
|
|
1812
|
+
const n100Multiplier = Math.max(0, this.difficultyAttributes.overallDifficulty > 0
|
|
1791
1813
|
? 1 -
|
|
1792
|
-
Math.pow(this.
|
|
1814
|
+
Math.pow(this.difficultyAttributes.overallDifficulty /
|
|
1815
|
+
13.33, 1.8)
|
|
1793
1816
|
: 1);
|
|
1794
|
-
const n50Multiplier = Math.max(0, this.
|
|
1817
|
+
const n50Multiplier = Math.max(0, this.difficultyAttributes.overallDifficulty > 0.0
|
|
1795
1818
|
? 1 -
|
|
1796
|
-
Math.pow(this.
|
|
1819
|
+
Math.pow(this.difficultyAttributes.overallDifficulty /
|
|
1820
|
+
13.33, 5)
|
|
1797
1821
|
: 1);
|
|
1798
1822
|
// As we're adding 100s and 50s to an approximated number of combo breaks, the result can be higher
|
|
1799
1823
|
// than total hits in specific scenarios (which breaks some calculations), so we need to clamp it.
|
|
1800
1824
|
this.effectiveMissCount = Math.min(this.effectiveMissCount +
|
|
1801
1825
|
this.computedAccuracy.n100 * n100Multiplier +
|
|
1802
|
-
this.computedAccuracy.n50 * n50Multiplier, this.
|
|
1826
|
+
this.computedAccuracy.n50 * n50Multiplier, this.totalHits);
|
|
1803
1827
|
}
|
|
1804
|
-
if (this.
|
|
1828
|
+
if (this.difficultyAttributes.sliderCount > 0) {
|
|
1805
1829
|
// We assume 15% of sliders in a beatmap are difficult since there's no way to tell from the performance calculator.
|
|
1806
|
-
const estimateDifficultSliders = this.
|
|
1830
|
+
const estimateDifficultSliders = this.difficultyAttributes.sliderCount * 0.15;
|
|
1807
1831
|
const estimateSliderEndsDropped = osuBase.MathUtils.clamp(Math.min(this.computedAccuracy.n300 +
|
|
1808
1832
|
this.computedAccuracy.n50 +
|
|
1809
1833
|
this.computedAccuracy.nmiss, maxCombo - combo), 0, estimateDifficultSliders);
|
|
1810
1834
|
this.sliderNerfFactor =
|
|
1811
|
-
(1 - this.
|
|
1835
|
+
(1 - this.difficultyAttributes.sliderFactor) *
|
|
1812
1836
|
Math.pow(1 -
|
|
1813
1837
|
estimateSliderEndsDropped /
|
|
1814
1838
|
estimateDifficultSliders, 3) +
|
|
1815
|
-
this.
|
|
1839
|
+
this.difficultyAttributes.sliderFactor;
|
|
1816
1840
|
}
|
|
1817
1841
|
}
|
|
1818
1842
|
/**
|
|
@@ -1820,14 +1844,13 @@ class PerformanceCalculator {
|
|
|
1820
1844
|
*/
|
|
1821
1845
|
calculateEffectiveMissCount(combo, maxCombo) {
|
|
1822
1846
|
let comboBasedMissCount = 0;
|
|
1823
|
-
if (this.
|
|
1824
|
-
const fullComboThreshold = maxCombo -
|
|
1825
|
-
0.1 * this.difficultyCalculator.beatmap.hitObjects.sliders;
|
|
1847
|
+
if (this.difficultyAttributes.sliderCount > 0) {
|
|
1848
|
+
const fullComboThreshold = maxCombo - 0.1 * this.difficultyAttributes.sliderCount;
|
|
1826
1849
|
if (combo < fullComboThreshold) {
|
|
1827
1850
|
comboBasedMissCount = Math.min(fullComboThreshold / Math.max(1, combo), this.mode === osuBase.Modes.droid
|
|
1828
1851
|
? // We're clamping miss count because since it's derived from combo, it can
|
|
1829
1852
|
// be higher than the amount of objects and that breaks some calculations.
|
|
1830
|
-
this.
|
|
1853
|
+
this.totalHits
|
|
1831
1854
|
: // Clamp miss count to maximum amount of possible breaks.
|
|
1832
1855
|
this.computedAccuracy.n300 +
|
|
1833
1856
|
this.computedAccuracy.n100 +
|
|
@@ -1871,8 +1894,16 @@ class DroidPerformanceCalculator extends PerformanceCalculator {
|
|
|
1871
1894
|
return this._tapPenalty;
|
|
1872
1895
|
}
|
|
1873
1896
|
_tapPenalty = 1;
|
|
1897
|
+
difficultyAttributes;
|
|
1874
1898
|
finalMultiplier = 1.24;
|
|
1875
1899
|
mode = osuBase.Modes.droid;
|
|
1900
|
+
/**
|
|
1901
|
+
* @param difficultyAttributes The difficulty attributes to calculate.
|
|
1902
|
+
*/
|
|
1903
|
+
constructor(difficultyAttributes) {
|
|
1904
|
+
super();
|
|
1905
|
+
this.difficultyAttributes = osuBase.Utils.deepCopy(difficultyAttributes);
|
|
1906
|
+
}
|
|
1876
1907
|
/**
|
|
1877
1908
|
* Applies a tap penalty value to this calculator.
|
|
1878
1909
|
*
|
|
@@ -1914,24 +1945,23 @@ class DroidPerformanceCalculator extends PerformanceCalculator {
|
|
|
1914
1945
|
* Calculates the aim performance value of the beatmap.
|
|
1915
1946
|
*/
|
|
1916
1947
|
calculateAimValue() {
|
|
1917
|
-
|
|
1918
|
-
const objectCount = this.difficultyCalculator.objects.length;
|
|
1919
|
-
this.aim = this.baseValue(Math.pow(this.difficultyCalculator.aim, 0.8));
|
|
1948
|
+
this.aim = this.baseValue(Math.pow(this.difficultyAttributes.aimDifficulty, 0.8));
|
|
1920
1949
|
if (this.effectiveMissCount > 0) {
|
|
1921
1950
|
// Penalize misses by assessing # of misses relative to the total # of objects.
|
|
1922
1951
|
// Default a 3% reduction for any # of misses.
|
|
1923
1952
|
this.aim *=
|
|
1924
1953
|
0.97 *
|
|
1925
|
-
Math.pow(1 -
|
|
1954
|
+
Math.pow(1 -
|
|
1955
|
+
Math.pow(this.effectiveMissCount / this.totalHits, 0.775), this.effectiveMissCount);
|
|
1926
1956
|
}
|
|
1927
1957
|
// Combo scaling
|
|
1928
1958
|
this.aim *= this.comboPenalty;
|
|
1929
1959
|
// Scale the aim value with slider factor to nerf very likely dropped sliderends.
|
|
1930
1960
|
this.aim *= this.sliderNerfFactor;
|
|
1931
1961
|
// Scale the aim value with accuracy.
|
|
1932
|
-
this.aim *= this.computedAccuracy.value(
|
|
1962
|
+
this.aim *= this.computedAccuracy.value(this.totalHits);
|
|
1933
1963
|
// It is also important to consider accuracy difficulty when doing that.
|
|
1934
|
-
const od = this.
|
|
1964
|
+
const od = this.difficultyAttributes.overallDifficulty;
|
|
1935
1965
|
const odScaling = Math.pow(od, 2) / 2500;
|
|
1936
1966
|
this.aim *= 0.98 + (od >= 0 ? odScaling : -odScaling);
|
|
1937
1967
|
}
|
|
@@ -1939,15 +1969,14 @@ class DroidPerformanceCalculator extends PerformanceCalculator {
|
|
|
1939
1969
|
* Calculates the tap performance value of the beatmap.
|
|
1940
1970
|
*/
|
|
1941
1971
|
calculateTapValue() {
|
|
1942
|
-
|
|
1943
|
-
const objectCount = this.difficultyCalculator.objects.length;
|
|
1944
|
-
this.tap = this.baseValue(this.difficultyCalculator.tap);
|
|
1972
|
+
this.tap = this.baseValue(this.difficultyAttributes.tapDifficulty);
|
|
1945
1973
|
if (this.effectiveMissCount > 0) {
|
|
1946
1974
|
// Penalize misses by assessing # of misses relative to the total # of objects.
|
|
1947
1975
|
// Default a 3% reduction for any # of misses.
|
|
1948
1976
|
this.tap *=
|
|
1949
1977
|
0.97 *
|
|
1950
|
-
Math.pow(1 -
|
|
1978
|
+
Math.pow(1 -
|
|
1979
|
+
Math.pow(this.effectiveMissCount / this.totalHits, 0.775), Math.pow(this.effectiveMissCount, 0.875));
|
|
1951
1980
|
}
|
|
1952
1981
|
// Combo scaling
|
|
1953
1982
|
this.tap *= this.comboPenalty;
|
|
@@ -1955,7 +1984,7 @@ class DroidPerformanceCalculator extends PerformanceCalculator {
|
|
|
1955
1984
|
const countGreat = this.computedAccuracy.n300;
|
|
1956
1985
|
const countOk = this.computedAccuracy.n100;
|
|
1957
1986
|
const countMeh = this.computedAccuracy.n50;
|
|
1958
|
-
const relevantTotalDiff =
|
|
1987
|
+
const relevantTotalDiff = this.totalHits - this.difficultyAttributes.speedNoteCount;
|
|
1959
1988
|
const relevantAccuracy = new osuBase.Accuracy({
|
|
1960
1989
|
n300: Math.max(0, countGreat - relevantTotalDiff),
|
|
1961
1990
|
n100: Math.max(0, countOk - Math.max(0, relevantTotalDiff - countGreat)),
|
|
@@ -1963,15 +1992,15 @@ class DroidPerformanceCalculator extends PerformanceCalculator {
|
|
|
1963
1992
|
nmiss: this.effectiveMissCount,
|
|
1964
1993
|
});
|
|
1965
1994
|
// Scale the tap value with accuracy and OD.
|
|
1966
|
-
const od = this.
|
|
1995
|
+
const od = this.difficultyAttributes.overallDifficulty;
|
|
1967
1996
|
const odScaling = Math.pow(od, 2) / 750;
|
|
1968
1997
|
this.tap *=
|
|
1969
1998
|
(0.95 + (od > 0 ? odScaling : -odScaling)) *
|
|
1970
|
-
Math.pow((this.computedAccuracy.value(
|
|
1971
|
-
relevantAccuracy.value(this.
|
|
1999
|
+
Math.pow((this.computedAccuracy.value(this.totalHits) +
|
|
2000
|
+
relevantAccuracy.value(this.difficultyAttributes.speedNoteCount)) /
|
|
1972
2001
|
2, (14 - Math.max(od, 2.5)) / 2);
|
|
1973
2002
|
// Scale the tap value with # of 50s to punish doubletapping.
|
|
1974
|
-
this.tap *= Math.pow(0.99, Math.max(0, this.computedAccuracy.n50 -
|
|
2003
|
+
this.tap *= Math.pow(0.99, Math.max(0, this.computedAccuracy.n50 - this.totalHits / 500));
|
|
1975
2004
|
// Scale the tap value with three-fingered penalty.
|
|
1976
2005
|
this.tap /= this._tapPenalty;
|
|
1977
2006
|
}
|
|
@@ -1979,34 +2008,35 @@ class DroidPerformanceCalculator extends PerformanceCalculator {
|
|
|
1979
2008
|
* Calculates the accuracy performance value of the beatmap.
|
|
1980
2009
|
*/
|
|
1981
2010
|
calculateAccuracyValue() {
|
|
1982
|
-
if (this.
|
|
2011
|
+
if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModRelax)) {
|
|
2012
|
+
this.accuracy = 0;
|
|
1983
2013
|
return;
|
|
1984
2014
|
}
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
? objectCount -
|
|
1989
|
-
this.difficultyCalculator.beatmap.hitObjects.spinners
|
|
1990
|
-
: this.difficultyCalculator.beatmap.hitObjects.circles;
|
|
2015
|
+
const ncircles = this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModScoreV2)
|
|
2016
|
+
? this.totalHits - this.difficultyAttributes.spinnerCount
|
|
2017
|
+
: this.difficultyAttributes.hitCircleCount;
|
|
1991
2018
|
if (ncircles === 0) {
|
|
2019
|
+
this.accuracy = 0;
|
|
1992
2020
|
return;
|
|
1993
2021
|
}
|
|
1994
2022
|
const realAccuracy = new osuBase.Accuracy({
|
|
1995
2023
|
...this.computedAccuracy,
|
|
1996
|
-
n300: this.computedAccuracy.n300 - (
|
|
2024
|
+
n300: this.computedAccuracy.n300 - (this.totalHits - ncircles),
|
|
1997
2025
|
});
|
|
1998
2026
|
// Lots of arbitrary values from testing.
|
|
1999
2027
|
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
|
|
2000
2028
|
this.accuracy =
|
|
2001
|
-
Math.pow(1.4, this.
|
|
2029
|
+
Math.pow(1.4, this.difficultyAttributes.overallDifficulty) *
|
|
2002
2030
|
Math.pow(realAccuracy.value(ncircles), 12) *
|
|
2003
2031
|
10;
|
|
2004
2032
|
// Bonus for many hitcircles - it's harder to keep good accuracy up for longer
|
|
2005
2033
|
this.accuracy *= Math.min(1.15, Math.pow(ncircles / 1000, 0.3));
|
|
2006
2034
|
// Scale the accuracy value with rhythm complexity.
|
|
2007
2035
|
this.accuracy *=
|
|
2008
|
-
1.5 /
|
|
2009
|
-
|
|
2036
|
+
1.5 /
|
|
2037
|
+
(1 +
|
|
2038
|
+
Math.exp(-(this.difficultyAttributes.rhythmDifficulty - 1) / 2));
|
|
2039
|
+
if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModFlashlight)) {
|
|
2010
2040
|
this.accuracy *= 1.02;
|
|
2011
2041
|
}
|
|
2012
2042
|
}
|
|
@@ -2014,32 +2044,33 @@ class DroidPerformanceCalculator extends PerformanceCalculator {
|
|
|
2014
2044
|
* Calculates the flashlight performance value of the beatmap.
|
|
2015
2045
|
*/
|
|
2016
2046
|
calculateFlashlightValue() {
|
|
2017
|
-
if (!this.
|
|
2047
|
+
if (!this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModFlashlight)) {
|
|
2048
|
+
this.flashlight = 0;
|
|
2018
2049
|
return;
|
|
2019
2050
|
}
|
|
2020
|
-
// Global variables
|
|
2021
|
-
const objectCount = this.difficultyCalculator.objects.length;
|
|
2022
2051
|
this.flashlight =
|
|
2023
|
-
Math.pow(this.
|
|
2052
|
+
Math.pow(this.difficultyAttributes.flashlightDifficulty, 1.6) * 25;
|
|
2024
2053
|
// Combo scaling
|
|
2025
2054
|
this.flashlight *= this.comboPenalty;
|
|
2026
2055
|
if (this.effectiveMissCount > 0) {
|
|
2027
2056
|
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
|
|
2028
2057
|
this.flashlight *=
|
|
2029
2058
|
0.97 *
|
|
2030
|
-
Math.pow(1 -
|
|
2059
|
+
Math.pow(1 -
|
|
2060
|
+
Math.pow(this.effectiveMissCount / this.totalHits, 0.775), Math.pow(this.effectiveMissCount, 0.875));
|
|
2031
2061
|
}
|
|
2032
2062
|
// Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius.
|
|
2033
2063
|
this.flashlight *=
|
|
2034
2064
|
0.7 +
|
|
2035
|
-
0.1 * Math.min(1,
|
|
2036
|
-
(
|
|
2037
|
-
? 0.2 * Math.min(1, (
|
|
2065
|
+
0.1 * Math.min(1, this.totalHits / 200) +
|
|
2066
|
+
(this.totalHits > 200
|
|
2067
|
+
? 0.2 * Math.min(1, (this.totalHits - 200) / 200)
|
|
2038
2068
|
: 0);
|
|
2039
2069
|
// Scale the flashlight value with accuracy slightly.
|
|
2040
|
-
this.flashlight *=
|
|
2070
|
+
this.flashlight *=
|
|
2071
|
+
0.5 + this.computedAccuracy.value(this.totalHits) / 2;
|
|
2041
2072
|
// It is also important to consider accuracy difficulty when doing that.
|
|
2042
|
-
const od = this.
|
|
2073
|
+
const od = this.difficultyAttributes.overallDifficulty;
|
|
2043
2074
|
const odScaling = Math.pow(od, 2) / 2500;
|
|
2044
2075
|
this.flashlight *= 0.98 + (od >= 0 ? odScaling : -odScaling);
|
|
2045
2076
|
}
|
|
@@ -2047,25 +2078,25 @@ class DroidPerformanceCalculator extends PerformanceCalculator {
|
|
|
2047
2078
|
* Calculates the visual performance value of the beatmap.
|
|
2048
2079
|
*/
|
|
2049
2080
|
calculateVisualValue() {
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
this.visual = Math.pow(this.difficultyCalculator.visual, 1.6) * 22.5;
|
|
2081
|
+
this.visual =
|
|
2082
|
+
Math.pow(this.difficultyAttributes.visualDifficulty, 1.6) * 22.5;
|
|
2053
2083
|
if (this.effectiveMissCount > 0) {
|
|
2054
2084
|
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
|
|
2055
2085
|
this.visual *=
|
|
2056
2086
|
0.97 *
|
|
2057
|
-
Math.pow(1 -
|
|
2087
|
+
Math.pow(1 -
|
|
2088
|
+
Math.pow(this.effectiveMissCount / this.totalHits, 0.775), this.effectiveMissCount);
|
|
2058
2089
|
}
|
|
2059
2090
|
// Combo scaling
|
|
2060
2091
|
this.visual *= this.comboPenalty;
|
|
2061
2092
|
// Scale the visual value with object count to penalize short maps.
|
|
2062
2093
|
this.visual *= Math.min(1, 1.650668 +
|
|
2063
2094
|
(0.4845796 - 1.650668) /
|
|
2064
|
-
(1 + Math.pow(
|
|
2095
|
+
(1 + Math.pow(this.totalHits / 817.9306, 1.147469)));
|
|
2065
2096
|
// Scale the visual value with accuracy harshly.
|
|
2066
2097
|
this.visual *= Math.pow(this.computedAccuracy.value(), 8);
|
|
2067
2098
|
// It is also important to consider accuracy difficulty when doing that.
|
|
2068
|
-
const od = this.
|
|
2099
|
+
const od = this.difficultyAttributes.overallDifficulty;
|
|
2069
2100
|
const odScaling = Math.pow(od, 2) / 2500;
|
|
2070
2101
|
this.visual *= 0.98 + (od >= 0 ? odScaling : -odScaling);
|
|
2071
2102
|
}
|
|
@@ -2620,6 +2651,21 @@ class OsuDifficultyCalculator extends DifficultyCalculator {
|
|
|
2620
2651
|
* The flashlight star rating of the beatmap.
|
|
2621
2652
|
*/
|
|
2622
2653
|
flashlight = 0;
|
|
2654
|
+
attributes = {
|
|
2655
|
+
speedDifficulty: 0,
|
|
2656
|
+
mods: [],
|
|
2657
|
+
starRating: 0,
|
|
2658
|
+
maxCombo: 0,
|
|
2659
|
+
aimDifficulty: 0,
|
|
2660
|
+
flashlightDifficulty: 0,
|
|
2661
|
+
speedNoteCount: 0,
|
|
2662
|
+
sliderFactor: 0,
|
|
2663
|
+
approachRate: 0,
|
|
2664
|
+
overallDifficulty: 0,
|
|
2665
|
+
hitCircleCount: 0,
|
|
2666
|
+
sliderCount: 0,
|
|
2667
|
+
spinnerCount: 0,
|
|
2668
|
+
};
|
|
2623
2669
|
difficultyMultiplier = 0.0675;
|
|
2624
2670
|
mode = osuBase.Modes.osu;
|
|
2625
2671
|
/**
|
|
@@ -2734,6 +2780,7 @@ class OsuDifficultyCalculator extends DifficultyCalculator {
|
|
|
2734
2780
|
if (this.mods.some((m) => m instanceof osuBase.ModRelax)) {
|
|
2735
2781
|
this.aim *= 0.9;
|
|
2736
2782
|
}
|
|
2783
|
+
this.attributes.aimDifficulty = this.aim;
|
|
2737
2784
|
}
|
|
2738
2785
|
/**
|
|
2739
2786
|
* Called after speed skill calculation.
|
|
@@ -2742,7 +2789,7 @@ class OsuDifficultyCalculator extends DifficultyCalculator {
|
|
|
2742
2789
|
*/
|
|
2743
2790
|
postCalculateSpeed(speedSkill) {
|
|
2744
2791
|
this.strainPeaks.speed = speedSkill.strainPeaks;
|
|
2745
|
-
this.speed = this.starValue(speedSkill.difficultyValue());
|
|
2792
|
+
this.speed = this.attributes.speedDifficulty = this.starValue(speedSkill.difficultyValue());
|
|
2746
2793
|
}
|
|
2747
2794
|
/**
|
|
2748
2795
|
* Calculates speed-related attributes.
|
|
@@ -2768,6 +2815,7 @@ class OsuDifficultyCalculator extends DifficultyCalculator {
|
|
|
2768
2815
|
if (this.mods.some((m) => m instanceof osuBase.ModRelax)) {
|
|
2769
2816
|
this.flashlight *= 0.7;
|
|
2770
2817
|
}
|
|
2818
|
+
this.attributes.flashlightDifficulty = this.flashlight;
|
|
2771
2819
|
}
|
|
2772
2820
|
}
|
|
2773
2821
|
|
|
@@ -2832,8 +2880,16 @@ class OsuPerformanceCalculator extends PerformanceCalculator {
|
|
|
2832
2880
|
* The flashlight performance value.
|
|
2833
2881
|
*/
|
|
2834
2882
|
flashlight = 0;
|
|
2883
|
+
difficultyAttributes;
|
|
2835
2884
|
finalMultiplier = 1.14;
|
|
2836
2885
|
mode = osuBase.Modes.osu;
|
|
2886
|
+
/**
|
|
2887
|
+
* @param difficultyAttributes The difficulty attributes to calculate.
|
|
2888
|
+
*/
|
|
2889
|
+
constructor(difficultyAttributes) {
|
|
2890
|
+
super();
|
|
2891
|
+
this.difficultyAttributes = osuBase.Utils.deepCopy(difficultyAttributes);
|
|
2892
|
+
}
|
|
2837
2893
|
calculateValues() {
|
|
2838
2894
|
this.calculateAimValue();
|
|
2839
2895
|
this.calculateSpeedValue();
|
|
@@ -2851,25 +2907,24 @@ class OsuPerformanceCalculator extends PerformanceCalculator {
|
|
|
2851
2907
|
* Calculates the aim performance value of the beatmap.
|
|
2852
2908
|
*/
|
|
2853
2909
|
calculateAimValue() {
|
|
2854
|
-
|
|
2855
|
-
const objectCount = this.difficultyCalculator.objects.length;
|
|
2856
|
-
const calculatedAR = this.difficultyCalculator.stats.ar;
|
|
2857
|
-
this.aim = this.baseValue(this.difficultyCalculator.aim);
|
|
2910
|
+
this.aim = this.baseValue(this.difficultyAttributes.aimDifficulty);
|
|
2858
2911
|
// Longer maps are worth more
|
|
2859
|
-
let lengthBonus = 0.95 + 0.4 * Math.min(1,
|
|
2860
|
-
if (
|
|
2861
|
-
lengthBonus += Math.log10(
|
|
2912
|
+
let lengthBonus = 0.95 + 0.4 * Math.min(1, this.totalHits / 2000);
|
|
2913
|
+
if (this.totalHits > 2000) {
|
|
2914
|
+
lengthBonus += Math.log10(this.totalHits / 2000) * 0.5;
|
|
2862
2915
|
}
|
|
2863
2916
|
this.aim *= lengthBonus;
|
|
2864
2917
|
if (this.effectiveMissCount > 0) {
|
|
2865
2918
|
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
|
|
2866
2919
|
this.aim *=
|
|
2867
2920
|
0.97 *
|
|
2868
|
-
Math.pow(1 -
|
|
2921
|
+
Math.pow(1 -
|
|
2922
|
+
Math.pow(this.effectiveMissCount / this.totalHits, 0.775), this.effectiveMissCount);
|
|
2869
2923
|
}
|
|
2870
2924
|
// Combo scaling
|
|
2871
2925
|
this.aim *= this.comboPenalty;
|
|
2872
|
-
|
|
2926
|
+
const calculatedAR = this.difficultyAttributes.approachRate;
|
|
2927
|
+
if (!this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModRelax)) {
|
|
2873
2928
|
// AR scaling
|
|
2874
2929
|
let arFactor = 0;
|
|
2875
2930
|
if (calculatedAR > 10.33) {
|
|
@@ -2882,57 +2937,56 @@ class OsuPerformanceCalculator extends PerformanceCalculator {
|
|
|
2882
2937
|
this.aim *= 1 + arFactor * lengthBonus;
|
|
2883
2938
|
}
|
|
2884
2939
|
// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
|
|
2885
|
-
if (this.
|
|
2940
|
+
if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModHidden)) {
|
|
2886
2941
|
this.aim *= 1 + 0.04 * (12 - calculatedAR);
|
|
2887
2942
|
}
|
|
2888
2943
|
// Scale the aim value with slider factor to nerf very likely dropped sliderends.
|
|
2889
2944
|
this.aim *= this.sliderNerfFactor;
|
|
2890
2945
|
// Scale the aim value with accuracy.
|
|
2891
|
-
this.aim *= this.computedAccuracy.value(
|
|
2946
|
+
this.aim *= this.computedAccuracy.value(this.totalHits);
|
|
2892
2947
|
// It is also important to consider accuracy difficulty when doing that.
|
|
2893
|
-
const odScaling = Math.pow(this.
|
|
2948
|
+
const odScaling = Math.pow(this.difficultyAttributes.overallDifficulty, 2) / 2500;
|
|
2894
2949
|
this.aim *= 0.98 + odScaling;
|
|
2895
2950
|
}
|
|
2896
2951
|
/**
|
|
2897
2952
|
* Calculates the speed performance value of the beatmap.
|
|
2898
2953
|
*/
|
|
2899
2954
|
calculateSpeedValue() {
|
|
2900
|
-
if (this.
|
|
2955
|
+
if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModRelax)) {
|
|
2901
2956
|
this.speed = 0;
|
|
2902
2957
|
return;
|
|
2903
2958
|
}
|
|
2904
2959
|
// Global variables
|
|
2905
|
-
|
|
2906
|
-
const calculatedAR = this.difficultyCalculator.stats.ar;
|
|
2907
|
-
const n50 = this.computedAccuracy.n50;
|
|
2908
|
-
this.speed = this.baseValue(this.difficultyCalculator.speed);
|
|
2960
|
+
this.speed = this.baseValue(this.difficultyAttributes.speedDifficulty);
|
|
2909
2961
|
// Longer maps are worth more
|
|
2910
|
-
let lengthBonus = 0.95 + 0.4 * Math.min(1,
|
|
2911
|
-
if (
|
|
2912
|
-
lengthBonus += Math.log10(
|
|
2962
|
+
let lengthBonus = 0.95 + 0.4 * Math.min(1, this.totalHits / 2000);
|
|
2963
|
+
if (this.totalHits > 2000) {
|
|
2964
|
+
lengthBonus += Math.log10(this.totalHits / 2000) * 0.5;
|
|
2913
2965
|
}
|
|
2914
2966
|
this.speed *= lengthBonus;
|
|
2915
2967
|
if (this.effectiveMissCount > 0) {
|
|
2916
2968
|
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
|
|
2917
2969
|
this.speed *=
|
|
2918
2970
|
0.97 *
|
|
2919
|
-
Math.pow(1 -
|
|
2971
|
+
Math.pow(1 -
|
|
2972
|
+
Math.pow(this.effectiveMissCount / this.totalHits, 0.775), Math.pow(this.effectiveMissCount, 0.875));
|
|
2920
2973
|
}
|
|
2921
2974
|
// Combo scaling
|
|
2922
2975
|
this.speed *= this.comboPenalty;
|
|
2923
2976
|
// AR scaling
|
|
2977
|
+
const calculatedAR = this.difficultyAttributes.approachRate;
|
|
2924
2978
|
if (calculatedAR > 10.33) {
|
|
2925
2979
|
// Buff for longer maps with high AR.
|
|
2926
2980
|
this.speed *= 1 + 0.3 * (calculatedAR - 10.33) * lengthBonus;
|
|
2927
2981
|
}
|
|
2928
|
-
if (this.
|
|
2982
|
+
if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModHidden)) {
|
|
2929
2983
|
this.speed *= 1 + 0.04 * (12 - calculatedAR);
|
|
2930
2984
|
}
|
|
2931
2985
|
// Calculate accuracy assuming the worst case scenario.
|
|
2932
2986
|
const countGreat = this.computedAccuracy.n300;
|
|
2933
2987
|
const countOk = this.computedAccuracy.n100;
|
|
2934
2988
|
const countMeh = this.computedAccuracy.n50;
|
|
2935
|
-
const relevantTotalDiff =
|
|
2989
|
+
const relevantTotalDiff = this.totalHits - this.difficultyAttributes.speedNoteCount;
|
|
2936
2990
|
const relevantAccuracy = new osuBase.Accuracy({
|
|
2937
2991
|
n300: Math.max(0, countGreat - relevantTotalDiff),
|
|
2938
2992
|
n100: Math.max(0, countOk - Math.max(0, relevantTotalDiff - countGreat)),
|
|
@@ -2941,45 +2995,48 @@ class OsuPerformanceCalculator extends PerformanceCalculator {
|
|
|
2941
2995
|
});
|
|
2942
2996
|
// Scale the speed value with accuracy and OD.
|
|
2943
2997
|
this.speed *=
|
|
2944
|
-
(0.95 +
|
|
2945
|
-
Math.pow(
|
|
2998
|
+
(0.95 +
|
|
2999
|
+
Math.pow(this.difficultyAttributes.overallDifficulty, 2) /
|
|
3000
|
+
750) *
|
|
3001
|
+
Math.pow((this.computedAccuracy.value(this.totalHits) +
|
|
2946
3002
|
relevantAccuracy.value()) /
|
|
2947
|
-
2, (14.5 -
|
|
3003
|
+
2, (14.5 -
|
|
3004
|
+
Math.max(this.difficultyAttributes.overallDifficulty, 8)) /
|
|
3005
|
+
2);
|
|
2948
3006
|
// Scale the speed value with # of 50s to punish doubletapping.
|
|
2949
|
-
this.speed *= Math.pow(0.99, Math.max(0, n50 -
|
|
3007
|
+
this.speed *= Math.pow(0.99, Math.max(0, this.computedAccuracy.n50 - this.totalHits / 500));
|
|
2950
3008
|
}
|
|
2951
3009
|
/**
|
|
2952
3010
|
* Calculates the accuracy performance value of the beatmap.
|
|
2953
3011
|
*/
|
|
2954
3012
|
calculateAccuracyValue() {
|
|
2955
|
-
if (this.
|
|
3013
|
+
if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModRelax)) {
|
|
3014
|
+
this.accuracy = 0;
|
|
2956
3015
|
return;
|
|
2957
3016
|
}
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
? nobjects - this.difficultyCalculator.beatmap.hitObjects.spinners
|
|
2962
|
-
: this.difficultyCalculator.beatmap.hitObjects.circles;
|
|
3017
|
+
const ncircles = this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModScoreV2)
|
|
3018
|
+
? this.totalHits - this.difficultyAttributes.spinnerCount
|
|
3019
|
+
: this.difficultyAttributes.hitCircleCount;
|
|
2963
3020
|
if (ncircles === 0) {
|
|
3021
|
+
this.accuracy = 0;
|
|
2964
3022
|
return;
|
|
2965
3023
|
}
|
|
2966
3024
|
const realAccuracy = new osuBase.Accuracy({
|
|
2967
3025
|
...this.computedAccuracy,
|
|
2968
|
-
n300: this.computedAccuracy.n300 -
|
|
2969
|
-
(this.difficultyCalculator.objects.length - ncircles),
|
|
3026
|
+
n300: this.computedAccuracy.n300 - (this.totalHits - ncircles),
|
|
2970
3027
|
});
|
|
2971
3028
|
// Lots of arbitrary values from testing.
|
|
2972
3029
|
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
|
|
2973
3030
|
this.accuracy =
|
|
2974
|
-
Math.pow(1.52163, this.
|
|
3031
|
+
Math.pow(1.52163, this.difficultyAttributes.overallDifficulty) *
|
|
2975
3032
|
Math.pow(realAccuracy.value(ncircles), 24) *
|
|
2976
3033
|
2.83;
|
|
2977
3034
|
// Bonus for many hitcircles - it's harder to keep good accuracy up for longer
|
|
2978
3035
|
this.accuracy *= Math.min(1.15, Math.pow(ncircles / 1000, 0.3));
|
|
2979
|
-
if (this.
|
|
3036
|
+
if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModHidden)) {
|
|
2980
3037
|
this.accuracy *= 1.08;
|
|
2981
3038
|
}
|
|
2982
|
-
if (this.
|
|
3039
|
+
if (this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModFlashlight)) {
|
|
2983
3040
|
this.accuracy *= 1.02;
|
|
2984
3041
|
}
|
|
2985
3042
|
}
|
|
@@ -2987,32 +3044,34 @@ class OsuPerformanceCalculator extends PerformanceCalculator {
|
|
|
2987
3044
|
* Calculates the flashlight performance value of the beatmap.
|
|
2988
3045
|
*/
|
|
2989
3046
|
calculateFlashlightValue() {
|
|
2990
|
-
if (!this.
|
|
3047
|
+
if (!this.difficultyAttributes.mods.some((m) => m instanceof osuBase.ModFlashlight)) {
|
|
3048
|
+
this.flashlight = 0;
|
|
2991
3049
|
return;
|
|
2992
3050
|
}
|
|
2993
3051
|
// Global variables
|
|
2994
|
-
const objectCount = this.difficultyCalculator.objects.length;
|
|
2995
3052
|
this.flashlight =
|
|
2996
|
-
Math.pow(this.
|
|
3053
|
+
Math.pow(this.difficultyAttributes.flashlightDifficulty, 2) * 25;
|
|
2997
3054
|
// Combo scaling
|
|
2998
3055
|
this.flashlight *= this.comboPenalty;
|
|
2999
3056
|
if (this.effectiveMissCount > 0) {
|
|
3000
3057
|
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
|
|
3001
3058
|
this.flashlight *=
|
|
3002
3059
|
0.97 *
|
|
3003
|
-
Math.pow(1 -
|
|
3060
|
+
Math.pow(1 -
|
|
3061
|
+
Math.pow(this.effectiveMissCount / this.totalHits, 0.775), Math.pow(this.effectiveMissCount, 0.875));
|
|
3004
3062
|
}
|
|
3005
3063
|
// Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius.
|
|
3006
3064
|
this.flashlight *=
|
|
3007
3065
|
0.7 +
|
|
3008
|
-
0.1 * Math.min(1,
|
|
3009
|
-
(
|
|
3010
|
-
? 0.2 * Math.min(1, (
|
|
3066
|
+
0.1 * Math.min(1, this.totalHits / 200) +
|
|
3067
|
+
(this.totalHits > 200
|
|
3068
|
+
? 0.2 * Math.min(1, (this.totalHits - 200) / 200)
|
|
3011
3069
|
: 0);
|
|
3012
3070
|
// Scale the flashlight value with accuracy slightly.
|
|
3013
|
-
this.flashlight *=
|
|
3071
|
+
this.flashlight *=
|
|
3072
|
+
0.5 + this.computedAccuracy.value(this.totalHits) / 2;
|
|
3014
3073
|
// It is also important to consider accuracy difficulty when doing that.
|
|
3015
|
-
const odScaling = Math.pow(this.
|
|
3074
|
+
const odScaling = Math.pow(this.difficultyAttributes.overallDifficulty, 2) / 2500;
|
|
3016
3075
|
this.flashlight *= 0.98 + odScaling;
|
|
3017
3076
|
}
|
|
3018
3077
|
toString() {
|