@rian8337/osu-base 2.0.0-alpha.25 → 2.0.0-alpha.31
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 +258 -10
- package/dist/index.js +111 -43
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/typings/index.d.ts +68 -11
package/README.md
CHANGED
|
@@ -4,11 +4,33 @@ The base module required for all my osu! modules.
|
|
|
4
4
|
|
|
5
5
|
# Features
|
|
6
6
|
|
|
7
|
-
This module
|
|
7
|
+
This module serves as the foundation of all my osu! modules. It provides multiple features such as:
|
|
8
|
+
|
|
9
|
+
- Accuracy calculator and estimator
|
|
10
|
+
- Calculate an accuracy value from hit values or estimate hit values from an accuracy value.
|
|
11
|
+
- API request builders
|
|
12
|
+
- Supports osu!droid API and osu! API v1.
|
|
13
|
+
- Beatmap decoder
|
|
14
|
+
- Fully decodes an `.osu` file into a `Beatmap` object that is easier to work with.
|
|
15
|
+
- Beatmap encoder
|
|
16
|
+
- Will encode a beatmap into an `.osu` file with format version 14.
|
|
17
|
+
- Hit window converters
|
|
18
|
+
- Convert an OD value to its hit window values and vice versa.
|
|
19
|
+
- Map statistics calculator
|
|
20
|
+
- Calculate a given map statistics (CS, AR, OD, and HP) with modifications applied (mods, custom speed multiplier, etc).
|
|
21
|
+
- Mod conversion utilities
|
|
22
|
+
- Convert a mod combination string in osu!droid (i.e. `hr`) and osu!standard (i.e. `HDHR`) into an array of mods.
|
|
23
|
+
- Available mods can be looked at the documentation.
|
|
24
|
+
- Storyboard decoder
|
|
25
|
+
- Supports `.osb` and `.osu` files.
|
|
26
|
+
- Storyboard encoder
|
|
27
|
+
- Storyboard will be encoded into a format that is supported by `.osu` files (that is, all variable instances will be replaced).
|
|
8
28
|
|
|
9
29
|
# Specific Requirements
|
|
10
30
|
|
|
11
|
-
If you want to
|
|
31
|
+
If you want to use the osu!droid API, you need to have an osu!droid API key set as `DROID_API_KEY` environment variable.
|
|
32
|
+
|
|
33
|
+
If you want to use the osu! API, you need to have an osu! API key set as `OSU_API_KEY` environment variable.
|
|
12
34
|
|
|
13
35
|
See usage for more details.
|
|
14
36
|
|
|
@@ -26,7 +48,82 @@ yarn add @rian8337/osu-base
|
|
|
26
48
|
|
|
27
49
|
# Usage
|
|
28
50
|
|
|
29
|
-
##
|
|
51
|
+
## Accuracy Calculator and Estimator
|
|
52
|
+
|
|
53
|
+
### Calculate Accuracy Value (0-1) from Hit Values
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
import { Accuracy } from "@rian8337/osu-base";
|
|
57
|
+
|
|
58
|
+
// If you specified the amount of great hits (`n300`).
|
|
59
|
+
let accuracyValue = new Accuracy({
|
|
60
|
+
n300: 1000,
|
|
61
|
+
n100: 125,
|
|
62
|
+
n50: 10,
|
|
63
|
+
nmiss: 5,
|
|
64
|
+
}).value();
|
|
65
|
+
|
|
66
|
+
console.log(accuracyValue);
|
|
67
|
+
|
|
68
|
+
// If you didn't specify the amount of great hits (`n300`).
|
|
69
|
+
const objectCount = 2000;
|
|
70
|
+
|
|
71
|
+
accuracyValue = new Accuracy({
|
|
72
|
+
n100: 125,
|
|
73
|
+
n50: 10,
|
|
74
|
+
nmiss: 5,
|
|
75
|
+
}).value(objectCount);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Hit Values Estimation
|
|
79
|
+
|
|
80
|
+
The way this estimation works is that the estimator will see if the accuracy can be estimated by just using good hit values. Otherwise, the estimator will use meh hit values.
|
|
81
|
+
|
|
82
|
+
Keep in mind that it will not use the miss hit value.
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
import { Accuracy } from "@rian8337/osu-base";
|
|
86
|
+
|
|
87
|
+
const accuracy = new Accuracy({
|
|
88
|
+
percent: 99.7,
|
|
89
|
+
nobjects: 2000,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
console.log(accuracy.n300);
|
|
93
|
+
console.log(accuracy.n100);
|
|
94
|
+
console.log(accuracy.n50);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## API Request Builders
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
import {
|
|
101
|
+
DroidAPIRequestBuilder,
|
|
102
|
+
OsuAPIRequestBuilder,
|
|
103
|
+
} from "@rian8337/osu-base";
|
|
104
|
+
|
|
105
|
+
const droidBuilder = new DroidAPIRequestBuilder()
|
|
106
|
+
.setEndpoint("getuserinfo.php")
|
|
107
|
+
.addParameter("uid", 51076);
|
|
108
|
+
|
|
109
|
+
const droidResult = await droidBuilder.sendRequest();
|
|
110
|
+
|
|
111
|
+
console.log(droidResult);
|
|
112
|
+
|
|
113
|
+
const osuBuilder = new OsuAPIRequestBuilder()
|
|
114
|
+
.setEndpoint("get_beatmaps")
|
|
115
|
+
.addParamter("b", 901854);
|
|
116
|
+
|
|
117
|
+
const osuResult = await osuBuilder.sendRequest();
|
|
118
|
+
|
|
119
|
+
console.log(osuResult);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Beatmap Decoder
|
|
123
|
+
|
|
124
|
+
There are two primary ways of using the beatmap decoder.
|
|
125
|
+
|
|
126
|
+
### Using osu! API
|
|
30
127
|
|
|
31
128
|
```js
|
|
32
129
|
import { MapInfo } from "@rian8337/osu-base";
|
|
@@ -47,22 +144,173 @@ if (!beatmapInfo.title) {
|
|
|
47
144
|
console.log(beatmapInfo.map);
|
|
48
145
|
```
|
|
49
146
|
|
|
50
|
-
|
|
147
|
+
#### Not retrieving beatmap file
|
|
51
148
|
|
|
52
|
-
You can also opt out from downloading the beatmap (`.osu`) file if you just want to retrieve information from the API by setting `file` to `false`, however a
|
|
149
|
+
You can also opt out from downloading the beatmap (`.osu`) file if you just want to retrieve information from the API by setting `file` to `false`, however a decoded beatmap will not be provided.
|
|
53
150
|
|
|
54
|
-
|
|
151
|
+
### Not using osu! API
|
|
55
152
|
|
|
56
153
|
```js
|
|
57
154
|
import { readFile } from "fs";
|
|
58
|
-
import {
|
|
155
|
+
import { BeatmapDecoder } from "@rian8337/osu-base";
|
|
59
156
|
|
|
60
157
|
readFile("path/to/file.osu", { encoding: "utf-8" }, (err, data) => {
|
|
61
158
|
if (err) throw err;
|
|
62
159
|
|
|
63
|
-
const
|
|
160
|
+
const decoder = new BeatmapDecoder().decode(data);
|
|
64
161
|
|
|
65
|
-
//
|
|
66
|
-
console.log(
|
|
162
|
+
// Decoded beatmap can be accessed via the `result` field
|
|
163
|
+
console.log(decoder.result);
|
|
67
164
|
});
|
|
68
165
|
```
|
|
166
|
+
|
|
167
|
+
## Beatmap Encoder
|
|
168
|
+
|
|
169
|
+
```js
|
|
170
|
+
import { Beatmap, BeatmapEncoder } from "@rian8337/osu-base";
|
|
171
|
+
|
|
172
|
+
const beatmap = new Beatmap();
|
|
173
|
+
|
|
174
|
+
const encoder = new BeatmapEncoder(beatmap).encode();
|
|
175
|
+
|
|
176
|
+
// Encoded beatmap can be accessed via the `result` field
|
|
177
|
+
console.log(encoder.result);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Hit Window Converters
|
|
181
|
+
|
|
182
|
+
### OD to Hit Window
|
|
183
|
+
|
|
184
|
+
```js
|
|
185
|
+
import { DroidHitWindow, OsuHitWindow } from "@rian8337/osu-base";
|
|
186
|
+
|
|
187
|
+
// Convert OD to osu!droid hit window
|
|
188
|
+
const droidWindow = new DroidHitWindow(10);
|
|
189
|
+
|
|
190
|
+
console.log(droidHitWindow.hitWindowFor300());
|
|
191
|
+
console.log(droidHitWindow.hitWindowFor100());
|
|
192
|
+
console.log(droidHitWindow.hitWindowFor50());
|
|
193
|
+
|
|
194
|
+
// Calculating for the Precise mod in mind is also possible
|
|
195
|
+
console.log(droidHitWindow.hitWindowFor300(true));
|
|
196
|
+
console.log(droidHitWindow.hitWindowFor100(true));
|
|
197
|
+
console.log(droidHitWindow.hitWindowFor50(true));
|
|
198
|
+
|
|
199
|
+
// Convert OD to osu!standard hit window
|
|
200
|
+
const osuWindow = new OsuHitWindow(10);
|
|
201
|
+
|
|
202
|
+
console.log(osuWindow.hitWindowFor300());
|
|
203
|
+
console.log(osuWindow.hitWindowFor100());
|
|
204
|
+
console.log(osuWindow.hitWindowFor50());
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Hit Window to OD
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
import { DroidHitWindow, OsuHitWindow } from "@rian8337/osu-base";
|
|
211
|
+
|
|
212
|
+
// Convert hit window to osu!droid OD
|
|
213
|
+
console.log(DroidHitWindow.hitWindow300ToOD(50));
|
|
214
|
+
console.log(DroidHitWindow.hitWindow100ToOD(100));
|
|
215
|
+
console.log(DroidHitWindow.hitWindow50ToOD(200));
|
|
216
|
+
|
|
217
|
+
// Calculating for the Precise mod in mind is also possible
|
|
218
|
+
console.log(DroidHitWindow.hitWindow300ToOD(25, true));
|
|
219
|
+
console.log(DroidHitWindow.hitWindow100ToOD(80, true));
|
|
220
|
+
console.log(DroidHitWindow.hitWindow50ToOD(130, true));
|
|
221
|
+
|
|
222
|
+
// Convert hit window to osu!standard OD
|
|
223
|
+
console.log(OsuHitWindow.hitWindow300ToOD(20));
|
|
224
|
+
console.log(OsuHitWindow.hitWindow100ToOD(60));
|
|
225
|
+
console.log(OsuHitWindow.hitWindow50ToOD(100));
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Map Statistics Calculator
|
|
229
|
+
|
|
230
|
+
The map statistics calculator can only be used once per instance.
|
|
231
|
+
|
|
232
|
+
### General Usage
|
|
233
|
+
|
|
234
|
+
```js
|
|
235
|
+
import { MapStats } from "@rian8337/osu-base";
|
|
236
|
+
|
|
237
|
+
const stats = new MapStats({
|
|
238
|
+
cs: 4,
|
|
239
|
+
ar: 9,
|
|
240
|
+
od: 8,
|
|
241
|
+
hp: 6,
|
|
242
|
+
}).calculate();
|
|
243
|
+
|
|
244
|
+
console.log(stats);
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Every value is optional.
|
|
248
|
+
|
|
249
|
+
### Available Options
|
|
250
|
+
|
|
251
|
+
You can specify more options to alter the final result of the calculation:
|
|
252
|
+
|
|
253
|
+
- Mods
|
|
254
|
+
- Custom speed multiplier
|
|
255
|
+
- Force AR (whether to keep the AR at its original value)
|
|
256
|
+
- Game mode (switch between osu!droid and osu!standard, defaults to osu!standard)
|
|
257
|
+
|
|
258
|
+
```js
|
|
259
|
+
import { MapStats, ModUtil, modes } from "@rian8337/osu-base";
|
|
260
|
+
|
|
261
|
+
const stats = new MapStats({
|
|
262
|
+
cs: 4,
|
|
263
|
+
ar: 9,
|
|
264
|
+
od: 8,
|
|
265
|
+
hp: 6,
|
|
266
|
+
mods: ModUtil.pcStringToMods("HDHR"),
|
|
267
|
+
speedMultiplier: 1.25,
|
|
268
|
+
isForceAR: true,
|
|
269
|
+
}).calculate({ mode: modes.osu });
|
|
270
|
+
|
|
271
|
+
console.log(stats);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Mod Conversion Utilities
|
|
275
|
+
|
|
276
|
+
```js
|
|
277
|
+
import { ModUtil } from "@rian8337/osu-base";
|
|
278
|
+
|
|
279
|
+
// Convert droid mod string into an array of mods
|
|
280
|
+
console.log(ModUtil.droidStringToMods("hr"));
|
|
281
|
+
|
|
282
|
+
// Convert PC modbits into an array of mods
|
|
283
|
+
console.log(ModUtil.pcModbitsToMods(12));
|
|
284
|
+
|
|
285
|
+
// Convert PC mod string into an array mods
|
|
286
|
+
console.log(ModUtil.pcStringToMods("HDHR"));
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Storyboard Decoder
|
|
290
|
+
|
|
291
|
+
```js
|
|
292
|
+
import { readFile } from "fs";
|
|
293
|
+
import { StoryboardDecoder } from "@rian8337/osu-base";
|
|
294
|
+
|
|
295
|
+
readFile("path/to/file.osb", { encoding: "utf-8" }, (err, data) => {
|
|
296
|
+
if (err) throw err;
|
|
297
|
+
|
|
298
|
+
const decoder = new StoryboardDecoder().decode(data);
|
|
299
|
+
|
|
300
|
+
// Decoded storyboard can be accessed via the `result` field
|
|
301
|
+
console.log(decoder.result);
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Storyboard Encoder
|
|
306
|
+
|
|
307
|
+
```js
|
|
308
|
+
import { Storyboard, StoryboardEncoder } from "@rian8337/osu-base";
|
|
309
|
+
|
|
310
|
+
const storyboard = new Storyboard();
|
|
311
|
+
|
|
312
|
+
const encoder = new StoryboardEncoder(storyboard).encode();
|
|
313
|
+
|
|
314
|
+
// Encoded storyboard can be accessed via the `result` field
|
|
315
|
+
console.log(encoder.result);
|
|
316
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -815,9 +815,7 @@ class Slider extends HitObject {
|
|
|
815
815
|
startTime: finalSpanEndTime,
|
|
816
816
|
});
|
|
817
817
|
this.nestedHitObjects.push(this.tail);
|
|
818
|
-
this.nestedHitObjects.sort((a, b) =>
|
|
819
|
-
return a.startTime - b.startTime;
|
|
820
|
-
});
|
|
818
|
+
this.nestedHitObjects.sort((a, b) => a.startTime - b.startTime);
|
|
821
819
|
}
|
|
822
820
|
toString() {
|
|
823
821
|
return `Position: [${this.position.x}, ${this.position.y}], distance: ${this.path.expectedDistance}, repetitions: ${this.repetitions}, slider ticks: ${this.nestedHitObjects.filter((v) => v instanceof SliderTick).length}`;
|
|
@@ -1872,6 +1870,51 @@ class HitWindow {
|
|
|
1872
1870
|
* Represents the hit window of osu!droid.
|
|
1873
1871
|
*/
|
|
1874
1872
|
class DroidHitWindow extends HitWindow {
|
|
1873
|
+
/**
|
|
1874
|
+
* Calculates the overall difficulty value of a great hit window.
|
|
1875
|
+
*
|
|
1876
|
+
* @param value The value of the hit window, in milliseconds.
|
|
1877
|
+
* @param isPrecise Whether to calculate for Precise mod.
|
|
1878
|
+
* @returns The overall difficulty value.
|
|
1879
|
+
*/
|
|
1880
|
+
static hitWindow300ToOD(value, isPrecise) {
|
|
1881
|
+
if (isPrecise) {
|
|
1882
|
+
return 5 - (value - 55) / 6;
|
|
1883
|
+
}
|
|
1884
|
+
else {
|
|
1885
|
+
return 5 - (value - 75) / 5;
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
/**
|
|
1889
|
+
* Calculates the overall difficulty value of a good hit window.
|
|
1890
|
+
*
|
|
1891
|
+
* @param value The value of the hit window, in milliseconds.
|
|
1892
|
+
* @param isPrecise Whether to calculate for Precise mod.
|
|
1893
|
+
* @returns The overall difficulty value.
|
|
1894
|
+
*/
|
|
1895
|
+
static hitWindow100ToOD(value, isPrecise) {
|
|
1896
|
+
if (isPrecise) {
|
|
1897
|
+
return 5 - (value - 120) / 8;
|
|
1898
|
+
}
|
|
1899
|
+
else {
|
|
1900
|
+
return 5 - (value - 150) / 10;
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
/**
|
|
1904
|
+
* Calculates the overall difficulty value of a meh hit window.
|
|
1905
|
+
*
|
|
1906
|
+
* @param value The value of the hit window, in milliseconds.
|
|
1907
|
+
* @param isPrecise Whether to calculate for Precise mod.
|
|
1908
|
+
* @returns The overall difficulty value.
|
|
1909
|
+
*/
|
|
1910
|
+
static hitWindow50ToOD(value, isPrecise) {
|
|
1911
|
+
if (isPrecise) {
|
|
1912
|
+
return 5 - (value - 180) / 10;
|
|
1913
|
+
}
|
|
1914
|
+
else {
|
|
1915
|
+
return 5 - (value - 250) / 10;
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1875
1918
|
hitWindowFor300(isPrecise) {
|
|
1876
1919
|
if (isPrecise) {
|
|
1877
1920
|
return 55 + 6 * (5 - this.overallDifficulty);
|
|
@@ -1901,6 +1944,33 @@ class DroidHitWindow extends HitWindow {
|
|
|
1901
1944
|
* Represents the hit window of osu!standard.
|
|
1902
1945
|
*/
|
|
1903
1946
|
class OsuHitWindow extends HitWindow {
|
|
1947
|
+
/**
|
|
1948
|
+
* Calculates the overall difficulty value of a great hit window.
|
|
1949
|
+
*
|
|
1950
|
+
* @param value The value of the hit window, in milliseconds.
|
|
1951
|
+
* @returns The overall difficulty value.
|
|
1952
|
+
*/
|
|
1953
|
+
static hitWindow300ToOD(value) {
|
|
1954
|
+
return (80 - value) / 6;
|
|
1955
|
+
}
|
|
1956
|
+
/**
|
|
1957
|
+
* Calculates the overall difficulty value of a good hit window.
|
|
1958
|
+
*
|
|
1959
|
+
* @param value The value of the hit window, in milliseconds.
|
|
1960
|
+
* @returns The overall difficulty value.
|
|
1961
|
+
*/
|
|
1962
|
+
static hitWindow100ToOD(value) {
|
|
1963
|
+
return (140 - value) / 8;
|
|
1964
|
+
}
|
|
1965
|
+
/**
|
|
1966
|
+
* Calculates the overall difficulty value of a meh hit window.
|
|
1967
|
+
*
|
|
1968
|
+
* @param value The value of the hit window, in milliseconds.
|
|
1969
|
+
* @returns The overall difficulty value.
|
|
1970
|
+
*/
|
|
1971
|
+
static hitWindow50ToOD(value) {
|
|
1972
|
+
return (200 - value) / 10;
|
|
1973
|
+
}
|
|
1904
1974
|
hitWindowFor300() {
|
|
1905
1975
|
return 80 - 6 * this.overallDifficulty;
|
|
1906
1976
|
}
|
|
@@ -2076,6 +2146,8 @@ class ModFlashlight extends Mod {
|
|
|
2076
2146
|
* Represents the Hidden mod.
|
|
2077
2147
|
*/
|
|
2078
2148
|
class ModHidden extends Mod {
|
|
2149
|
+
static fadeInDurationMultiplier = 0.4;
|
|
2150
|
+
static fadeOutDurationMultiplier = 0.3;
|
|
2079
2151
|
scoreMultiplier = 1.06;
|
|
2080
2152
|
acronym = "HD";
|
|
2081
2153
|
name = "Hidden";
|
|
@@ -2234,9 +2306,7 @@ class ModUtil {
|
|
|
2234
2306
|
* Mods that change the way the map looks.
|
|
2235
2307
|
*/
|
|
2236
2308
|
static mapChangingMods = [
|
|
2237
|
-
|
|
2238
|
-
new ModNightCore(),
|
|
2239
|
-
new ModHalfTime(),
|
|
2309
|
+
...this.speedChangingMods,
|
|
2240
2310
|
new ModEasy(),
|
|
2241
2311
|
new ModHardRock(),
|
|
2242
2312
|
new ModSmallCircle(),
|
|
@@ -2394,29 +2464,31 @@ class MapStats {
|
|
|
2394
2464
|
}
|
|
2395
2465
|
switch (params?.mode ?? exports.modes.osu) {
|
|
2396
2466
|
case exports.modes.droid:
|
|
2397
|
-
// In droid pre-1.6.8, NC speed multiplier is assumed bugged (1.39)
|
|
2467
|
+
// In droid pre-1.6.8, NC speed multiplier is assumed bugged (1.39).
|
|
2398
2468
|
if (this.mods.some((m) => m instanceof ModNightCore) &&
|
|
2399
2469
|
this.oldStatistics) {
|
|
2400
2470
|
this.speedMultiplier *= 1.39 / 1.5;
|
|
2401
2471
|
}
|
|
2402
2472
|
// CS and OD work differently in droid, therefore it
|
|
2403
2473
|
// needs to be computed regardless of map-changing mods
|
|
2404
|
-
// and statistics multiplier
|
|
2474
|
+
// and statistics multiplier.
|
|
2405
2475
|
if (this.od !== undefined) {
|
|
2406
|
-
//
|
|
2476
|
+
// Apply EZ or HR to OD.
|
|
2407
2477
|
this.od = Math.min(this.od * statisticsMultiplier, 10);
|
|
2408
|
-
//
|
|
2478
|
+
// Convert original OD to droid hit window to take
|
|
2479
|
+
// droid hit window and the PR mod in mind.
|
|
2409
2480
|
const droidToMS = new DroidHitWindow(this.od).hitWindowFor300(this.mods.some((m) => m instanceof ModPrecise)) / this.speedMultiplier;
|
|
2410
|
-
|
|
2481
|
+
// Convert droid hit window back to original OD.
|
|
2482
|
+
this.od = OsuHitWindow.hitWindow300ToOD(droidToMS);
|
|
2411
2483
|
}
|
|
2412
2484
|
// HR and EZ works differently in droid in terms of
|
|
2413
|
-
// CS modification (even CS in itself as well)
|
|
2485
|
+
// CS modification (even CS in itself as well).
|
|
2414
2486
|
//
|
|
2415
2487
|
// If present mods are found, they need to be removed
|
|
2416
2488
|
// from the bitwise enum of mods to prevent double
|
|
2417
|
-
// calculation
|
|
2489
|
+
// calculation.
|
|
2418
2490
|
if (this.cs !== undefined) {
|
|
2419
|
-
// Assume 681 is height
|
|
2491
|
+
// Assume 681 is height.
|
|
2420
2492
|
const assumedHeight = 681;
|
|
2421
2493
|
let scale = ((assumedHeight / 480) * (54.42 - this.cs * 4.48) * 2) /
|
|
2422
2494
|
128 +
|
|
@@ -5226,6 +5298,15 @@ class Encoder {
|
|
|
5226
5298
|
* The result of the encoding process.
|
|
5227
5299
|
*/
|
|
5228
5300
|
finalResult = "";
|
|
5301
|
+
/**
|
|
5302
|
+
* The result of the encoding process.
|
|
5303
|
+
*/
|
|
5304
|
+
get result() {
|
|
5305
|
+
return this.finalResult;
|
|
5306
|
+
}
|
|
5307
|
+
/**
|
|
5308
|
+
* @param target The target of the encoding process.
|
|
5309
|
+
*/
|
|
5229
5310
|
constructor(target) {
|
|
5230
5311
|
this.target = target;
|
|
5231
5312
|
}
|
|
@@ -5237,7 +5318,7 @@ class Encoder {
|
|
|
5237
5318
|
encode() {
|
|
5238
5319
|
this.reset();
|
|
5239
5320
|
this.encodeInternal();
|
|
5240
|
-
return this
|
|
5321
|
+
return this;
|
|
5241
5322
|
}
|
|
5242
5323
|
/**
|
|
5243
5324
|
* Writes a line to encoded text.
|
|
@@ -5715,7 +5796,7 @@ class BeatmapEventsEncoder extends BeatmapBaseEncoder {
|
|
|
5715
5796
|
this.writeLine(`2,${b.startTime},${b.endTime}`);
|
|
5716
5797
|
}
|
|
5717
5798
|
if (this.map.events.storyboard) {
|
|
5718
|
-
this.writeLine(new StoryboardEncoder(this.map.events.storyboard, false).encode());
|
|
5799
|
+
this.writeLine(new StoryboardEncoder(this.map.events.storyboard, false).encode().result);
|
|
5719
5800
|
}
|
|
5720
5801
|
else {
|
|
5721
5802
|
this.writeLine("//Storyboard Layer 0 (Background)");
|
|
@@ -6314,9 +6395,9 @@ class MapInfo {
|
|
|
6314
6395
|
* - Option `0`: return map title and mods used if defined
|
|
6315
6396
|
* - Option `1`: return song source and map download link to beatmap mirrors
|
|
6316
6397
|
* - Option `2`: return circle, slider, and spinner count
|
|
6317
|
-
* - Option `3`: return CS, AR, OD, and
|
|
6318
|
-
* - Option `4`: return CS, AR, OD, and
|
|
6319
|
-
* - Option `5`: return BPM, map length,
|
|
6398
|
+
* - Option `3`: return CS, AR, OD, HP, and max score statistics for droid
|
|
6399
|
+
* - Option `4`: return CS, AR, OD, HP, and max score statistics for PC
|
|
6400
|
+
* - Option `5`: return BPM, map length, and max combo
|
|
6320
6401
|
* - Option `6`: return last update date and map status
|
|
6321
6402
|
* - Option `7`: return favorite count and play count
|
|
6322
6403
|
*
|
|
@@ -6404,6 +6485,7 @@ class MapInfo {
|
|
|
6404
6485
|
droidModifiedStats.ar = MathUtils.round(droidModifiedStats.ar, 2);
|
|
6405
6486
|
droidModifiedStats.od = MathUtils.round(droidModifiedStats.od, 2);
|
|
6406
6487
|
droidModifiedStats.hp = MathUtils.round(droidModifiedStats.hp, 2);
|
|
6488
|
+
const maxScore = this.map?.maxDroidScore(new MapStats(mapParams)) ?? 0;
|
|
6407
6489
|
return `**CS**: ${droidOriginalStats.cs}${Precision.almostEqualsNumber(droidOriginalStats.cs, droidModifiedStats.cs)
|
|
6408
6490
|
? ""
|
|
6409
6491
|
: ` (${droidModifiedStats.cs})`} - **AR**: ${droidOriginalStats.ar}${Precision.almostEqualsNumber(droidOriginalStats.ar, droidModifiedStats.ar)
|
|
@@ -6412,7 +6494,9 @@ class MapInfo {
|
|
|
6412
6494
|
? ""
|
|
6413
6495
|
: ` (${droidModifiedStats.od})`} - **HP**: ${droidOriginalStats.hp}${Precision.almostEqualsNumber(droidOriginalStats.hp, droidModifiedStats.hp)
|
|
6414
6496
|
? ""
|
|
6415
|
-
: ` (${droidModifiedStats.hp})`}
|
|
6497
|
+
: ` (${droidModifiedStats.hp})`}${maxScore > 0
|
|
6498
|
+
? `\n**Max Score**: ${maxScore.toLocaleString()}`
|
|
6499
|
+
: ""}`;
|
|
6416
6500
|
}
|
|
6417
6501
|
case 4: {
|
|
6418
6502
|
const mapStatistics = new MapStats(mapParams).calculate();
|
|
@@ -6420,6 +6504,7 @@ class MapInfo {
|
|
|
6420
6504
|
mapStatistics.ar = MathUtils.round(mapStatistics.ar, 2);
|
|
6421
6505
|
mapStatistics.od = MathUtils.round(mapStatistics.od, 2);
|
|
6422
6506
|
mapStatistics.hp = MathUtils.round(mapStatistics.hp, 2);
|
|
6507
|
+
const maxScore = this.map?.maxOsuScore(mapStatistics.mods) ?? 0;
|
|
6423
6508
|
return `**CS**: ${this.cs}${Precision.almostEqualsNumber(this.cs, mapStatistics.cs)
|
|
6424
6509
|
? ""
|
|
6425
6510
|
: ` (${mapStatistics.cs})`} - **AR**: ${this.ar}${Precision.almostEqualsNumber(this.ar, mapStatistics.ar)
|
|
@@ -6428,12 +6513,12 @@ class MapInfo {
|
|
|
6428
6513
|
? ""
|
|
6429
6514
|
: ` (${mapStatistics.od})`} - **HP**: ${this.hp}${Precision.almostEqualsNumber(this.hp, mapStatistics.hp)
|
|
6430
6515
|
? ""
|
|
6431
|
-
: ` (${mapStatistics.hp})`}
|
|
6516
|
+
: ` (${mapStatistics.hp})`}${maxScore > 0
|
|
6517
|
+
? `\n**Max Score**: ${maxScore.toLocaleString()}`
|
|
6518
|
+
: ""}`;
|
|
6432
6519
|
}
|
|
6433
6520
|
case 5: {
|
|
6434
6521
|
const mapStatistics = new MapStats(mapParams).calculate();
|
|
6435
|
-
const droidMaxScore = this.map?.maxDroidScore(mapStatistics) ?? 0;
|
|
6436
|
-
const osuMaxScore = this.map?.maxOsuScore(mapStatistics.mods) ?? 0;
|
|
6437
6522
|
const convertedBPM = this.convertBPM(mapStatistics);
|
|
6438
6523
|
let string = "**BPM**: ";
|
|
6439
6524
|
if (this.map) {
|
|
@@ -6441,12 +6526,7 @@ class MapInfo {
|
|
|
6441
6526
|
if (uninheritedTimingPoints.length === 1) {
|
|
6442
6527
|
string += `${this.bpm}${!Precision.almostEqualsNumber(this.bpm, convertedBPM)
|
|
6443
6528
|
? ` (${convertedBPM})`
|
|
6444
|
-
: ""} - **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x
|
|
6445
|
-
? `\n**Max Droid Score**: ${droidMaxScore.toLocaleString()}`
|
|
6446
|
-
: ""}${osuMaxScore > 0
|
|
6447
|
-
? (droidMaxScore > 0 ? " - " : "\n") +
|
|
6448
|
-
`**Max Standard Score**: ${osuMaxScore.toLocaleString()}`
|
|
6449
|
-
: ""}`;
|
|
6529
|
+
: ""} - **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x`;
|
|
6450
6530
|
}
|
|
6451
6531
|
else {
|
|
6452
6532
|
let maxBPM = this.bpm;
|
|
@@ -6473,25 +6553,13 @@ class MapInfo {
|
|
|
6473
6553
|
string += `(${convertedBPM}) `;
|
|
6474
6554
|
}
|
|
6475
6555
|
}
|
|
6476
|
-
string += `- **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x
|
|
6477
|
-
? `\n**Max Droid Score**: ${droidMaxScore.toLocaleString()}`
|
|
6478
|
-
: ""}${osuMaxScore > 0
|
|
6479
|
-
? (droidMaxScore > 0 ? " - " : "\n") +
|
|
6480
|
-
`**Max Standard Score**: ${osuMaxScore.toLocaleString()}`
|
|
6481
|
-
: ""}`;
|
|
6556
|
+
string += `- **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x`;
|
|
6482
6557
|
}
|
|
6483
6558
|
}
|
|
6484
6559
|
else {
|
|
6485
6560
|
string += `${this.bpm}${!Precision.almostEqualsNumber(this.bpm, convertedBPM)
|
|
6486
6561
|
? ` (${convertedBPM})`
|
|
6487
|
-
: ""} - **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x
|
|
6488
|
-
? `${droidMaxScore > 0
|
|
6489
|
-
? `\n**Max Droid Score**: ${droidMaxScore.toLocaleString()}`
|
|
6490
|
-
: ""}${osuMaxScore > 0
|
|
6491
|
-
? (droidMaxScore > 0 ? " - " : "\n") +
|
|
6492
|
-
`**Max Standard Score**: ${osuMaxScore.toLocaleString()}`
|
|
6493
|
-
: ""}`
|
|
6494
|
-
: ""}`;
|
|
6562
|
+
: ""} - **Length**: ${this.convertTime(mapStatistics)} - **Max Combo**: ${this.maxCombo}x`;
|
|
6495
6563
|
}
|
|
6496
6564
|
return string;
|
|
6497
6565
|
}
|