sonolus-next-rush-plus-engine 1.3.7 → 1.3.8
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/EngineWatchData +0 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +17 -7
- package/dist/mmw/analyze.d.ts +7 -1
- package/dist/mmw/analyze.js +74 -36
- package/dist/mmw/convert.d.ts +5 -0
- package/dist/mmw/convert.js +532 -12
- package/package.json +1 -1
package/dist/EngineWatchData
CHANGED
|
Binary file
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { LevelData } from '@sonolus/core';
|
|
2
2
|
import { mmwsToUSC } from './mmw/convert.js';
|
|
3
|
+
import { ucmmwsToLevelData } from './mmw/convert.js';
|
|
3
4
|
import { susToUSC } from './sus/convert.js';
|
|
4
5
|
import { uscToLevelData } from './usc/convert.js';
|
|
5
6
|
import { USC } from './usc/index.js';
|
|
6
|
-
export { susToUSC, mmwsToUSC, uscToLevelData };
|
|
7
|
+
export { susToUSC, mmwsToUSC, uscToLevelData, ucmmwsToLevelData };
|
|
7
8
|
export * from './usc/index.js';
|
|
8
9
|
export declare const convertToLevelData: (input: string | Uint8Array | USC | LevelData, offset?: number) => LevelData;
|
|
9
|
-
export declare const version = "1.3.
|
|
10
|
+
export declare const version = "1.3.8";
|
|
10
11
|
export declare const databaseEngineItem: {
|
|
11
12
|
readonly name: "next-rush-plus";
|
|
12
13
|
readonly version: 13;
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { detectMMWSType } from './mmw/analyze.js';
|
|
2
2
|
import { mmwsToUSC } from './mmw/convert.js';
|
|
3
|
+
import { ucmmwsToLevelData } from './mmw/convert.js';
|
|
3
4
|
import { susToUSC } from './sus/convert.js';
|
|
4
5
|
import { uscToLevelData } from './usc/convert.js';
|
|
5
6
|
import { isUSC } from './usc/analyze.js';
|
|
6
7
|
import { isLevelData } from './LevelData/analyze.js';
|
|
7
8
|
import { isPJSK } from './pjsk/analyze.js';
|
|
8
9
|
import { pjskToUSC } from './pjsk/convert.js';
|
|
9
|
-
export { susToUSC, mmwsToUSC, uscToLevelData };
|
|
10
|
+
export { susToUSC, mmwsToUSC, uscToLevelData, ucmmwsToLevelData };
|
|
10
11
|
export * from './usc/index.js';
|
|
11
12
|
export const convertToLevelData = (input, offset = 0) => {
|
|
12
13
|
if (isLevelData(input)) {
|
|
@@ -17,11 +18,20 @@ export const convertToLevelData = (input, offset = 0) => {
|
|
|
17
18
|
usc = input;
|
|
18
19
|
}
|
|
19
20
|
else if (input instanceof Uint8Array) {
|
|
20
|
-
|
|
21
|
-
if (
|
|
22
|
-
return
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
const type = detectMMWSType(input);
|
|
22
|
+
if (type === 'UCMMWS') {
|
|
23
|
+
return ucmmwsToLevelData(input);
|
|
24
|
+
}
|
|
25
|
+
if (type === 'MMWS' || type === 'CCMMWS') {
|
|
26
|
+
usc = mmwsToUSC(input);
|
|
27
|
+
if (type === 'MMWS') {
|
|
28
|
+
return uscToLevelData(usc, offset, true, true);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
return uscToLevelData(usc, offset, false, false);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
throw new Error('Unsupported Uint8Array type for MMWS');
|
|
25
35
|
}
|
|
26
36
|
else if (typeof input === 'string') {
|
|
27
37
|
try {
|
|
@@ -49,7 +59,7 @@ export const convertToLevelData = (input, offset = 0) => {
|
|
|
49
59
|
}
|
|
50
60
|
return uscToLevelData(usc, offset, true, true);
|
|
51
61
|
};
|
|
52
|
-
export const version = '1.3.
|
|
62
|
+
export const version = '1.3.8';
|
|
53
63
|
export const databaseEngineItem = {
|
|
54
64
|
name: 'next-rush-plus',
|
|
55
65
|
version: 13,
|
package/dist/mmw/analyze.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const FlickType: readonly ["none", "up", "left", "right"];
|
|
1
|
+
declare const FlickType: readonly ["none", "up", "left", "right", "down", "down_left", "down_right"];
|
|
2
2
|
type FlickType = (typeof FlickType)[number];
|
|
3
3
|
declare const StepType: readonly ["visible", "hidden", "ignored"];
|
|
4
4
|
type StepType = (typeof StepType)[number];
|
|
@@ -27,6 +27,9 @@ export type Score = {
|
|
|
27
27
|
tick: number;
|
|
28
28
|
speed: number;
|
|
29
29
|
layer: number;
|
|
30
|
+
skip?: number;
|
|
31
|
+
ease?: number;
|
|
32
|
+
hideNotes?: boolean;
|
|
30
33
|
}[];
|
|
31
34
|
skills: number[];
|
|
32
35
|
fever: {
|
|
@@ -42,6 +45,7 @@ export type Score = {
|
|
|
42
45
|
flags: {
|
|
43
46
|
critical: boolean;
|
|
44
47
|
friction: boolean;
|
|
48
|
+
dummy?: boolean;
|
|
45
49
|
};
|
|
46
50
|
layer: number;
|
|
47
51
|
}[];
|
|
@@ -50,6 +54,7 @@ export type Score = {
|
|
|
50
54
|
startHidden: boolean;
|
|
51
55
|
endHidden: boolean;
|
|
52
56
|
guide: boolean;
|
|
57
|
+
dummy?: boolean;
|
|
53
58
|
};
|
|
54
59
|
start: {
|
|
55
60
|
tick: number;
|
|
@@ -92,6 +97,7 @@ export type Score = {
|
|
|
92
97
|
flags: {
|
|
93
98
|
critical: boolean;
|
|
94
99
|
friction: boolean;
|
|
100
|
+
dummy?: boolean;
|
|
95
101
|
};
|
|
96
102
|
layer: number;
|
|
97
103
|
}[];
|
package/dist/mmw/analyze.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BinarySeeker } from '../lib/binaryseeker/mod.js';
|
|
2
|
-
const FlickType = ['none', 'up', 'left', 'right'];
|
|
2
|
+
const FlickType = ['none', 'up', 'left', 'right', 'down', 'down_left', 'down_right'];
|
|
3
3
|
const StepType = ['visible', 'hidden', 'ignored'];
|
|
4
4
|
const EaseType = ['linear', 'easeIn', 'easeOut', 'easeInOut', 'easeOutIn'];
|
|
5
5
|
/** Detect MMWS Type */
|
|
@@ -11,16 +11,27 @@ export const detectMMWSType = (mmws) => {
|
|
|
11
11
|
export const analyze = (mmws) => {
|
|
12
12
|
const buffer = new BinarySeeker(mmws.buffer);
|
|
13
13
|
const header = buffer.readString();
|
|
14
|
-
if (header !== 'MMWS' && header !== 'CCMMWS') {
|
|
14
|
+
if (header !== 'MMWS' && header !== 'CCMMWS' && header !== 'UCMMWS') {
|
|
15
15
|
throw new Error('Invalid MMWS file');
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
let cyanvasVersion =
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
let version = 0;
|
|
18
|
+
let cyanvasVersion = 0;
|
|
19
|
+
let untitledVersion = 0;
|
|
20
|
+
if (header === 'UCMMWS') {
|
|
21
|
+
untitledVersion = buffer.readInt32LE();
|
|
22
|
+
cyanvasVersion = 6;
|
|
23
|
+
version = 4;
|
|
24
|
+
}
|
|
25
|
+
else if (header === 'CCMMWS') {
|
|
26
|
+
cyanvasVersion = Math.max(buffer.readInt16LE(), 1);
|
|
27
|
+
version = buffer.readInt16LE();
|
|
21
28
|
}
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
else {
|
|
30
|
+
version = buffer.readInt16LE();
|
|
31
|
+
cyanvasVersion = buffer.readInt16LE();
|
|
32
|
+
}
|
|
33
|
+
if (version < 3 && header !== 'UCMMWS') {
|
|
34
|
+
throw new Error('Unsupported MMWS version');
|
|
24
35
|
}
|
|
25
36
|
const metadataPointer = buffer.readUInt32LE();
|
|
26
37
|
const eventsPointer = buffer.readUInt32LE();
|
|
@@ -28,7 +39,6 @@ export const analyze = (mmws) => {
|
|
|
28
39
|
const holdsPointer = buffer.readUInt32LE();
|
|
29
40
|
const damagesPointer = cyanvasVersion > 0 ? buffer.readUInt32LE() : 0;
|
|
30
41
|
const layersPointer = cyanvasVersion >= 4 ? buffer.readUInt32LE() : 0;
|
|
31
|
-
// const waypointsPointer = cyanvasVersion >= 5 ? buffer.readUInt32LE() : 0
|
|
32
42
|
buffer.seek(metadataPointer);
|
|
33
43
|
const metadata = {
|
|
34
44
|
title: buffer.readString(),
|
|
@@ -36,8 +46,11 @@ export const analyze = (mmws) => {
|
|
|
36
46
|
artist: buffer.readString(),
|
|
37
47
|
musicFile: buffer.readString(),
|
|
38
48
|
musicOffset: buffer.readFloat32LE(),
|
|
39
|
-
jacketFile: buffer.readString(),
|
|
49
|
+
jacketFile: version >= 2 ? buffer.readString() : '',
|
|
40
50
|
};
|
|
51
|
+
if (cyanvasVersion >= 1) {
|
|
52
|
+
buffer.readInt32LE();
|
|
53
|
+
}
|
|
41
54
|
buffer.seek(eventsPointer);
|
|
42
55
|
const events = {
|
|
43
56
|
timeSignatures: [],
|
|
@@ -64,29 +77,49 @@ export const analyze = (mmws) => {
|
|
|
64
77
|
bpm: buffer.readFloat32LE(),
|
|
65
78
|
});
|
|
66
79
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
tick
|
|
71
|
-
speed
|
|
72
|
-
layer
|
|
73
|
-
|
|
80
|
+
if (version >= 3) {
|
|
81
|
+
const hispeedChangesCount = buffer.readInt32LE();
|
|
82
|
+
for (let i = 0; i < hispeedChangesCount; i++) {
|
|
83
|
+
const tick = buffer.readInt32LE();
|
|
84
|
+
const speed = buffer.readFloat32LE();
|
|
85
|
+
const layer = cyanvasVersion >= 4 ? buffer.readInt32LE() : 0;
|
|
86
|
+
let skip = 0;
|
|
87
|
+
let ease = 0;
|
|
88
|
+
let hideNotes = false;
|
|
89
|
+
if (untitledVersion >= 2) {
|
|
90
|
+
skip = buffer.readFloat32LE();
|
|
91
|
+
ease = buffer.readInt16LE();
|
|
92
|
+
hideNotes = !!buffer.readInt16LE();
|
|
93
|
+
}
|
|
94
|
+
events.hispeedChanges.push({
|
|
95
|
+
tick,
|
|
96
|
+
speed,
|
|
97
|
+
layer,
|
|
98
|
+
skip,
|
|
99
|
+
ease,
|
|
100
|
+
hideNotes,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
74
103
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
104
|
+
if (version >= 2) {
|
|
105
|
+
const skillsCount = buffer.readInt32LE();
|
|
106
|
+
for (let i = 0; i < skillsCount; i++) {
|
|
107
|
+
const skillTick = buffer.readInt32LE();
|
|
108
|
+
events.skills.push(skillTick);
|
|
109
|
+
}
|
|
110
|
+
events.fever.start = buffer.readInt32LE();
|
|
111
|
+
events.fever.end = buffer.readInt32LE();
|
|
78
112
|
}
|
|
79
|
-
events.fever.start = buffer.readInt32LE();
|
|
80
|
-
events.fever.end = buffer.readInt32LE();
|
|
81
113
|
buffer.seek(tapsPointer);
|
|
82
114
|
const taps = [];
|
|
83
115
|
const tapsCount = buffer.readInt32LE();
|
|
84
116
|
for (let i = 0; i < tapsCount; i++) {
|
|
85
117
|
const tick = buffer.readInt32LE();
|
|
86
|
-
const lane = buffer.readInt32LE();
|
|
87
|
-
const width = buffer.readInt32LE();
|
|
118
|
+
const lane = cyanvasVersion >= 6 ? buffer.readFloat32LE() : buffer.readInt32LE();
|
|
119
|
+
const width = cyanvasVersion >= 6 ? buffer.readFloat32LE() : buffer.readInt32LE();
|
|
88
120
|
const layer = cyanvasVersion >= 4 ? buffer.readInt32LE() : 0;
|
|
89
|
-
const
|
|
121
|
+
const flickTypeRaw = buffer.readInt32LE();
|
|
122
|
+
const flickType = FlickType[flickTypeRaw] || 'none';
|
|
90
123
|
const flags = buffer.readInt32LE();
|
|
91
124
|
taps.push({
|
|
92
125
|
tick,
|
|
@@ -96,6 +129,7 @@ export const analyze = (mmws) => {
|
|
|
96
129
|
flags: {
|
|
97
130
|
critical: !!(flags & (1 << 0)),
|
|
98
131
|
friction: !!(flags & (1 << 1)),
|
|
132
|
+
dummy: cyanvasVersion >= 6 && untitledVersion >= 1 ? !!(flags & (1 << 2)) : false,
|
|
99
133
|
},
|
|
100
134
|
layer,
|
|
101
135
|
});
|
|
@@ -106,8 +140,8 @@ export const analyze = (mmws) => {
|
|
|
106
140
|
for (let i = 0; i < holdsCount; i++) {
|
|
107
141
|
const holdFlags = version >= 4 ? buffer.readInt32LE() : 0;
|
|
108
142
|
const startTick = buffer.readInt32LE();
|
|
109
|
-
const startLane = buffer.readInt32LE();
|
|
110
|
-
const startWidth = buffer.readInt32LE();
|
|
143
|
+
const startLane = cyanvasVersion >= 6 ? buffer.readFloat32LE() : buffer.readInt32LE();
|
|
144
|
+
const startWidth = cyanvasVersion >= 6 ? buffer.readFloat32LE() : buffer.readInt32LE();
|
|
111
145
|
const startLayer = cyanvasVersion >= 4 ? buffer.readInt32LE() : 0;
|
|
112
146
|
const startFlags = buffer.readInt32LE();
|
|
113
147
|
const startEase = EaseType[buffer.readInt32LE()];
|
|
@@ -117,10 +151,10 @@ export const analyze = (mmws) => {
|
|
|
117
151
|
const steps = [];
|
|
118
152
|
for (let j = 0; j < stepsCount; j++) {
|
|
119
153
|
const tick = buffer.readInt32LE();
|
|
120
|
-
const lane = buffer.readInt32LE();
|
|
121
|
-
const width = buffer.readInt32LE();
|
|
154
|
+
const lane = cyanvasVersion >= 6 ? buffer.readFloat32LE() : buffer.readInt32LE();
|
|
155
|
+
const width = cyanvasVersion >= 6 ? buffer.readFloat32LE() : buffer.readInt32LE();
|
|
122
156
|
const layer = cyanvasVersion >= 4 ? buffer.readInt32LE() : 0;
|
|
123
|
-
buffer.readInt32LE();
|
|
157
|
+
buffer.readInt32LE();
|
|
124
158
|
const type = StepType[buffer.readInt32LE()];
|
|
125
159
|
const ease = EaseType[buffer.readInt32LE()];
|
|
126
160
|
steps.push({
|
|
@@ -133,16 +167,18 @@ export const analyze = (mmws) => {
|
|
|
133
167
|
});
|
|
134
168
|
}
|
|
135
169
|
const endTick = buffer.readInt32LE();
|
|
136
|
-
const endLane = buffer.readInt32LE();
|
|
137
|
-
const endWidth = buffer.readInt32LE();
|
|
170
|
+
const endLane = cyanvasVersion >= 6 ? buffer.readFloat32LE() : buffer.readInt32LE();
|
|
171
|
+
const endWidth = cyanvasVersion >= 6 ? buffer.readFloat32LE() : buffer.readInt32LE();
|
|
138
172
|
const endLayer = cyanvasVersion >= 4 ? buffer.readInt32LE() : 0;
|
|
139
|
-
const
|
|
173
|
+
const endFlickTypeRaw = buffer.readInt32LE();
|
|
174
|
+
const endFlickType = FlickType[endFlickTypeRaw] || 'none';
|
|
140
175
|
const endFlags = buffer.readInt32LE();
|
|
141
176
|
holds.push({
|
|
142
177
|
flags: {
|
|
143
178
|
startHidden: !!(holdFlags & (1 << 0)),
|
|
144
179
|
endHidden: !!(holdFlags & (1 << 1)),
|
|
145
180
|
guide: !!(holdFlags & (1 << 2)),
|
|
181
|
+
dummy: !!(holdFlags & (1 << 3)),
|
|
146
182
|
},
|
|
147
183
|
start: {
|
|
148
184
|
tick: startTick,
|
|
@@ -183,10 +219,11 @@ export const analyze = (mmws) => {
|
|
|
183
219
|
const damagesCount = buffer.readInt32LE();
|
|
184
220
|
for (let i = 0; i < damagesCount; i++) {
|
|
185
221
|
const tick = buffer.readInt32LE();
|
|
186
|
-
const lane = buffer.readInt32LE();
|
|
187
|
-
const width = buffer.readInt32LE();
|
|
222
|
+
const lane = cyanvasVersion >= 6 ? buffer.readFloat32LE() : buffer.readInt32LE();
|
|
223
|
+
const width = cyanvasVersion >= 6 ? buffer.readFloat32LE() : buffer.readInt32LE();
|
|
188
224
|
const layer = cyanvasVersion >= 4 ? buffer.readInt32LE() : 0;
|
|
189
|
-
const
|
|
225
|
+
const flickTypeRaw = buffer.readInt32LE();
|
|
226
|
+
const flickType = FlickType[flickTypeRaw] || 'none';
|
|
190
227
|
const flags = buffer.readInt32LE();
|
|
191
228
|
damages.push({
|
|
192
229
|
tick,
|
|
@@ -197,6 +234,7 @@ export const analyze = (mmws) => {
|
|
|
197
234
|
flags: {
|
|
198
235
|
critical: !!(flags & (1 << 0)),
|
|
199
236
|
friction: !!(flags & (1 << 1)),
|
|
237
|
+
dummy: cyanvasVersion >= 6 && untitledVersion >= 1 ? !!(flags & (1 << 2)) : false,
|
|
200
238
|
},
|
|
201
239
|
});
|
|
202
240
|
}
|
package/dist/mmw/convert.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { USC } from '../usc/index.js';
|
|
2
|
+
import { type LevelData } from '@sonolus/core';
|
|
2
3
|
/**
|
|
3
4
|
* Convert MMWS or CCMMWS to a USC
|
|
4
5
|
*/
|
|
@@ -7,3 +8,7 @@ export declare const mmwsToUSC: (mmws: Uint8Array) => USC;
|
|
|
7
8
|
* Convert CCMMWS to a USC
|
|
8
9
|
*/
|
|
9
10
|
export declare const ccmmwsToUSC: (mmws: Uint8Array) => USC;
|
|
11
|
+
/**
|
|
12
|
+
* Convert UCMMWS to a LevelData
|
|
13
|
+
*/
|
|
14
|
+
export declare const ucmmwsToLevelData: (mmws: Uint8Array) => LevelData;
|
package/dist/mmw/convert.js
CHANGED
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
import { USCColor, } from '../usc/index.js';
|
|
2
2
|
import { analyze } from './analyze.js';
|
|
3
|
+
const SONOLUS_CONNECTOR_EASES = {
|
|
4
|
+
outin: 5,
|
|
5
|
+
out: 3,
|
|
6
|
+
linear: 1,
|
|
7
|
+
in: 2,
|
|
8
|
+
inout: 4,
|
|
9
|
+
};
|
|
10
|
+
const mmwEaseToSonolusEase = (ease) => {
|
|
11
|
+
switch (ease) {
|
|
12
|
+
case 'linear':
|
|
13
|
+
return SONOLUS_CONNECTOR_EASES.linear;
|
|
14
|
+
case 'easeIn':
|
|
15
|
+
return SONOLUS_CONNECTOR_EASES.in;
|
|
16
|
+
case 'easeOut':
|
|
17
|
+
return SONOLUS_CONNECTOR_EASES.out;
|
|
18
|
+
case 'easeInOut':
|
|
19
|
+
return SONOLUS_CONNECTOR_EASES.inout;
|
|
20
|
+
case 'easeOutIn':
|
|
21
|
+
return SONOLUS_CONNECTOR_EASES.outin;
|
|
22
|
+
default:
|
|
23
|
+
return SONOLUS_CONNECTOR_EASES.linear;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const EPSILON = 1e-6;
|
|
27
|
+
const TICKS_PER_BEAT = 480;
|
|
28
|
+
const laneToSonolusLane = (lane, width) => {
|
|
29
|
+
return lane - 6 + width / 2;
|
|
30
|
+
};
|
|
31
|
+
const flickTypeToDirection = (type) => {
|
|
32
|
+
switch (type) {
|
|
33
|
+
case 'up':
|
|
34
|
+
return 0;
|
|
35
|
+
case 'left':
|
|
36
|
+
return 1;
|
|
37
|
+
case 'right':
|
|
38
|
+
return 2;
|
|
39
|
+
case 'down':
|
|
40
|
+
return 3;
|
|
41
|
+
case 'down_left':
|
|
42
|
+
return 4;
|
|
43
|
+
case 'down_right':
|
|
44
|
+
return 5;
|
|
45
|
+
default:
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const resolveConnectorKind = (isCritical, isDummy) => {
|
|
50
|
+
if (isDummy) {
|
|
51
|
+
return isCritical ? 52 : 51;
|
|
52
|
+
}
|
|
53
|
+
return isCritical ? 1 : 0;
|
|
54
|
+
};
|
|
3
55
|
const mmwsEaseToUSCEase = {
|
|
4
56
|
linear: 'linear',
|
|
5
57
|
easeOut: 'out',
|
|
@@ -7,7 +59,6 @@ const mmwsEaseToUSCEase = {
|
|
|
7
59
|
easeInOut: 'inout',
|
|
8
60
|
easeOutIn: 'outin',
|
|
9
61
|
};
|
|
10
|
-
const ticksPerBeat = 480;
|
|
11
62
|
const laneToUSCLane = ({ lane, width }) => {
|
|
12
63
|
return lane - 6 + width / 2;
|
|
13
64
|
};
|
|
@@ -23,7 +74,7 @@ export const mmwsToUSC = (mmws) => {
|
|
|
23
74
|
for (const bpmChange of score.events.bpmChanges) {
|
|
24
75
|
usc.objects.push({
|
|
25
76
|
type: 'bpm',
|
|
26
|
-
beat: bpmChange.tick /
|
|
77
|
+
beat: bpmChange.tick / TICKS_PER_BEAT,
|
|
27
78
|
bpm: bpmChange.bpm,
|
|
28
79
|
});
|
|
29
80
|
}
|
|
@@ -34,10 +85,16 @@ export const mmwsToUSC = (mmws) => {
|
|
|
34
85
|
for (const hispeedChange of score.events.hispeedChanges) {
|
|
35
86
|
const key = hispeedChange.layer;
|
|
36
87
|
if (!tsGroups.has(key)) {
|
|
37
|
-
|
|
88
|
+
if (!tsGroups.has(0))
|
|
89
|
+
tsGroups.set(0, []);
|
|
90
|
+
tsGroups.get(0).push({
|
|
91
|
+
beat: hispeedChange.tick / TICKS_PER_BEAT,
|
|
92
|
+
timeScale: hispeedChange.speed,
|
|
93
|
+
});
|
|
94
|
+
continue;
|
|
38
95
|
}
|
|
39
96
|
tsGroups.get(key).push({
|
|
40
|
-
beat: hispeedChange.tick /
|
|
97
|
+
beat: hispeedChange.tick / TICKS_PER_BEAT,
|
|
41
98
|
timeScale: hispeedChange.speed,
|
|
42
99
|
});
|
|
43
100
|
}
|
|
@@ -50,14 +107,14 @@ export const mmwsToUSC = (mmws) => {
|
|
|
50
107
|
for (const tap of score.taps) {
|
|
51
108
|
const uscTap = {
|
|
52
109
|
type: 'single',
|
|
53
|
-
beat: tap.tick /
|
|
110
|
+
beat: tap.tick / TICKS_PER_BEAT,
|
|
54
111
|
timeScaleGroup: tap.layer,
|
|
55
112
|
critical: tap.flags.critical,
|
|
56
113
|
lane: laneToUSCLane(tap),
|
|
57
114
|
size: tap.width / 2,
|
|
58
115
|
trace: tap.flags.friction,
|
|
59
116
|
};
|
|
60
|
-
if (tap.flickType
|
|
117
|
+
if (tap.flickType === 'up' || tap.flickType === 'left' || tap.flickType === 'right') {
|
|
61
118
|
uscTap.direction = tap.flickType;
|
|
62
119
|
}
|
|
63
120
|
usc.objects.push(uscTap);
|
|
@@ -65,7 +122,7 @@ export const mmwsToUSC = (mmws) => {
|
|
|
65
122
|
for (const hold of score.holds) {
|
|
66
123
|
const uscStartNote = {
|
|
67
124
|
type: 'start',
|
|
68
|
-
beat: hold.start.tick /
|
|
125
|
+
beat: hold.start.tick / TICKS_PER_BEAT,
|
|
69
126
|
timeScaleGroup: hold.start.layer,
|
|
70
127
|
critical: hold.start.flags.critical,
|
|
71
128
|
ease: mmwsEaseToUSCEase[hold.start.ease],
|
|
@@ -79,14 +136,16 @@ export const mmwsToUSC = (mmws) => {
|
|
|
79
136
|
};
|
|
80
137
|
const uscEndNote = {
|
|
81
138
|
type: 'end',
|
|
82
|
-
beat: hold.end.tick /
|
|
139
|
+
beat: hold.end.tick / TICKS_PER_BEAT,
|
|
83
140
|
timeScaleGroup: hold.end.layer,
|
|
84
141
|
critical: hold.end.flags.critical,
|
|
85
142
|
lane: laneToUSCLane(hold.end),
|
|
86
143
|
size: hold.end.width / 2,
|
|
87
144
|
judgeType: hold.flags.endHidden ? 'none' : hold.end.flags.friction ? 'trace' : 'normal',
|
|
88
145
|
};
|
|
89
|
-
if (hold.end.flickType
|
|
146
|
+
if (hold.end.flickType === 'up' ||
|
|
147
|
+
hold.end.flickType === 'left' ||
|
|
148
|
+
hold.end.flickType === 'right') {
|
|
90
149
|
uscEndNote.direction = hold.end.flickType;
|
|
91
150
|
}
|
|
92
151
|
if (hold.flags.guide) {
|
|
@@ -95,7 +154,7 @@ export const mmwsToUSC = (mmws) => {
|
|
|
95
154
|
fade: hold.fadeType === 0 ? 'out' : hold.fadeType === 1 ? 'none' : 'in',
|
|
96
155
|
color: Object.entries(USCColor).find(([, i]) => i === hold.guideColor)[0],
|
|
97
156
|
midpoints: [hold.start, ...hold.steps, hold.end].map((step) => ({
|
|
98
|
-
beat: step.tick /
|
|
157
|
+
beat: step.tick / TICKS_PER_BEAT,
|
|
99
158
|
lane: laneToUSCLane(step),
|
|
100
159
|
size: step.width / 2,
|
|
101
160
|
timeScaleGroup: step.layer,
|
|
@@ -111,7 +170,7 @@ export const mmwsToUSC = (mmws) => {
|
|
|
111
170
|
connections: [
|
|
112
171
|
uscStartNote,
|
|
113
172
|
...hold.steps.map((step) => {
|
|
114
|
-
const beat = step.tick /
|
|
173
|
+
const beat = step.tick / TICKS_PER_BEAT;
|
|
115
174
|
const lane = laneToUSCLane(step);
|
|
116
175
|
const size = step.width / 2;
|
|
117
176
|
if (step.type === 'ignored') {
|
|
@@ -146,7 +205,7 @@ export const mmwsToUSC = (mmws) => {
|
|
|
146
205
|
for (const damage of score.damages) {
|
|
147
206
|
const uscDamage = {
|
|
148
207
|
type: 'damage',
|
|
149
|
-
beat: damage.tick /
|
|
208
|
+
beat: damage.tick / TICKS_PER_BEAT,
|
|
150
209
|
timeScaleGroup: damage.layer,
|
|
151
210
|
lane: laneToUSCLane(damage),
|
|
152
211
|
size: damage.width / 2,
|
|
@@ -159,3 +218,464 @@ export const mmwsToUSC = (mmws) => {
|
|
|
159
218
|
* Convert CCMMWS to a USC
|
|
160
219
|
*/
|
|
161
220
|
export const ccmmwsToUSC = mmwsToUSC;
|
|
221
|
+
/**
|
|
222
|
+
* Convert UCMMWS to a LevelData
|
|
223
|
+
*/
|
|
224
|
+
export const ucmmwsToLevelData = (mmws) => {
|
|
225
|
+
const score = analyze(mmws);
|
|
226
|
+
const entities = [];
|
|
227
|
+
const allIntermediateEntities = [];
|
|
228
|
+
const simLineEligibleNotes = [];
|
|
229
|
+
const timeScaleGroupIntermediates = [];
|
|
230
|
+
const createIntermediate = (archetype, data, isSimEligible = false) => {
|
|
231
|
+
const intermediateEntity = { archetype, data };
|
|
232
|
+
allIntermediateEntities.push(intermediateEntity);
|
|
233
|
+
if (isSimEligible) {
|
|
234
|
+
simLineEligibleNotes.push(intermediateEntity);
|
|
235
|
+
}
|
|
236
|
+
return intermediateEntity;
|
|
237
|
+
};
|
|
238
|
+
createIntermediate('Initialization', {});
|
|
239
|
+
createIntermediate('Stage', {});
|
|
240
|
+
if (score.events.bpmChanges.length === 0) {
|
|
241
|
+
createIntermediate('#BPM_CHANGE', { '#BEAT': 0, '#BPM': 120 });
|
|
242
|
+
}
|
|
243
|
+
for (const bpm of score.events.bpmChanges) {
|
|
244
|
+
createIntermediate('#BPM_CHANGE', {
|
|
245
|
+
'#BEAT': bpm.tick / TICKS_PER_BEAT,
|
|
246
|
+
'#BPM': bpm.bpm,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
for (const skillTick of score.events.skills) {
|
|
250
|
+
createIntermediate('Skill', { '#BEAT': skillTick / TICKS_PER_BEAT });
|
|
251
|
+
}
|
|
252
|
+
if (score.events.fever.start > 0) {
|
|
253
|
+
createIntermediate('FeverChance', { '#BEAT': score.events.fever.start / TICKS_PER_BEAT });
|
|
254
|
+
}
|
|
255
|
+
if (score.events.fever.end > 0) {
|
|
256
|
+
createIntermediate('FeverStart', { '#BEAT': score.events.fever.end / TICKS_PER_BEAT });
|
|
257
|
+
}
|
|
258
|
+
const layerChanges = new Map();
|
|
259
|
+
for (let i = 0; i < Math.max(1, score.numLayers); i++) {
|
|
260
|
+
layerChanges.set(i, []);
|
|
261
|
+
}
|
|
262
|
+
for (const hs of score.events.hispeedChanges) {
|
|
263
|
+
if (!layerChanges.has(hs.layer)) {
|
|
264
|
+
if (!layerChanges.has(0))
|
|
265
|
+
layerChanges.set(0, []);
|
|
266
|
+
layerChanges.get(0).push(hs);
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
layerChanges.get(hs.layer).push(hs);
|
|
270
|
+
}
|
|
271
|
+
for (let i = 0; i < Math.max(1, score.numLayers); i++) {
|
|
272
|
+
const changes = layerChanges.get(i) || [];
|
|
273
|
+
if (changes.length === 0 || changes[0].tick !== 0) {
|
|
274
|
+
changes.unshift({
|
|
275
|
+
tick: 0,
|
|
276
|
+
speed: 1,
|
|
277
|
+
layer: i,
|
|
278
|
+
skip: 0,
|
|
279
|
+
ease: 0,
|
|
280
|
+
hideNotes: false,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
changes.sort((a, b) => a.tick - b.tick);
|
|
284
|
+
const groupIntermediate = createIntermediate('#TIMESCALE_GROUP', {});
|
|
285
|
+
timeScaleGroupIntermediates[i] = groupIntermediate;
|
|
286
|
+
let lastChangeIntermediate = null;
|
|
287
|
+
for (const hs of changes) {
|
|
288
|
+
const data = {
|
|
289
|
+
'#BEAT': hs.tick / TICKS_PER_BEAT,
|
|
290
|
+
'#TIMESCALE': hs.speed === 0 ? 0.000001 : hs.speed,
|
|
291
|
+
'#TIMESCALE_SKIP': hs.skip || 0,
|
|
292
|
+
'#TIMESCALE_EASE': hs.ease || 0,
|
|
293
|
+
'#TIMESCALE_GROUP': groupIntermediate,
|
|
294
|
+
};
|
|
295
|
+
if (hs.hideNotes) {
|
|
296
|
+
data['hideNotes'] = 1;
|
|
297
|
+
}
|
|
298
|
+
const newChangeIntermediate = createIntermediate('#TIMESCALE_CHANGE', data);
|
|
299
|
+
if (lastChangeIntermediate === null) {
|
|
300
|
+
groupIntermediate.data['first'] = newChangeIntermediate;
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
lastChangeIntermediate.data['next'] = newChangeIntermediate;
|
|
304
|
+
}
|
|
305
|
+
lastChangeIntermediate = newChangeIntermediate;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
for (const tap of score.taps) {
|
|
309
|
+
const name_parts = [];
|
|
310
|
+
name_parts.push(tap.flags.critical ? 'Critical' : 'Normal');
|
|
311
|
+
if (tap.flickType !== 'none') {
|
|
312
|
+
name_parts.push(tap.flags.friction ? 'TraceFlick' : 'Flick');
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
name_parts.push(tap.flags.friction ? 'Trace' : 'Tap');
|
|
316
|
+
}
|
|
317
|
+
name_parts.push('Note');
|
|
318
|
+
const archetype = tap.flags.dummy ? `Fake${name_parts.join('')}` : name_parts.join('');
|
|
319
|
+
const timeScaleGroupRef = timeScaleGroupIntermediates[tap.layer] || timeScaleGroupIntermediates[0];
|
|
320
|
+
const data = {
|
|
321
|
+
'#BEAT': tap.tick / TICKS_PER_BEAT,
|
|
322
|
+
lane: laneToSonolusLane(tap.lane, tap.width),
|
|
323
|
+
size: tap.width,
|
|
324
|
+
isAttached: 0,
|
|
325
|
+
connectorEase: SONOLUS_CONNECTOR_EASES.linear,
|
|
326
|
+
isSeparator: 0,
|
|
327
|
+
segmentKind: tap.flags.critical ? 2 : 1,
|
|
328
|
+
segmentAlpha: 1,
|
|
329
|
+
'#TIMESCALE_GROUP': timeScaleGroupRef,
|
|
330
|
+
};
|
|
331
|
+
if (tap.flickType !== 'none') {
|
|
332
|
+
data['direction'] = flickTypeToDirection(tap.flickType);
|
|
333
|
+
}
|
|
334
|
+
createIntermediate(archetype, data, true);
|
|
335
|
+
}
|
|
336
|
+
for (const damage of score.damages) {
|
|
337
|
+
const archetype = damage.flags.dummy ? 'FakeDamageNote' : 'DamageNote';
|
|
338
|
+
const timeScaleGroupRef = timeScaleGroupIntermediates[damage.layer] || timeScaleGroupIntermediates[0];
|
|
339
|
+
createIntermediate(archetype, {
|
|
340
|
+
'#BEAT': damage.tick / TICKS_PER_BEAT,
|
|
341
|
+
lane: laneToSonolusLane(damage.lane, damage.width),
|
|
342
|
+
size: damage.width,
|
|
343
|
+
direction: 0,
|
|
344
|
+
isAttached: 0,
|
|
345
|
+
connectorEase: SONOLUS_CONNECTOR_EASES.linear,
|
|
346
|
+
isSeparator: 0,
|
|
347
|
+
segmentKind: 1,
|
|
348
|
+
segmentAlpha: 1,
|
|
349
|
+
'#TIMESCALE_GROUP': timeScaleGroupRef,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
for (const hold of score.holds) {
|
|
353
|
+
const timeScaleGroupRef = timeScaleGroupIntermediates[hold.start.layer] || timeScaleGroupIntermediates[0];
|
|
354
|
+
if (hold.flags.guide) {
|
|
355
|
+
const points = [hold.start, ...hold.steps, hold.end];
|
|
356
|
+
points.sort((a, b) => a.tick - b.tick);
|
|
357
|
+
let prevMidpointIntermediate = null;
|
|
358
|
+
let headMidpointIntermediate = null;
|
|
359
|
+
const guideConnectors = [];
|
|
360
|
+
const stepSize = Math.max(1, points.length - 1);
|
|
361
|
+
let stepIdx = 0;
|
|
362
|
+
for (const point of points) {
|
|
363
|
+
let segmentAlpha = 1;
|
|
364
|
+
if (hold.fadeType === 0) {
|
|
365
|
+
segmentAlpha = 1 - 0.8 * (stepIdx / stepSize);
|
|
366
|
+
}
|
|
367
|
+
else if (hold.fadeType === 2) {
|
|
368
|
+
segmentAlpha = 1 - 0.8 * ((stepSize - stepIdx) / stepSize);
|
|
369
|
+
}
|
|
370
|
+
const sonolusColorId = 101 + hold.guideColor;
|
|
371
|
+
const midpointIntermediate = createIntermediate('AnchorNote', {
|
|
372
|
+
'#BEAT': point.tick / TICKS_PER_BEAT,
|
|
373
|
+
lane: laneToSonolusLane(point.lane, point.width),
|
|
374
|
+
size: point.width,
|
|
375
|
+
direction: 0,
|
|
376
|
+
isAttached: 0,
|
|
377
|
+
connectorEase: 'ease' in point
|
|
378
|
+
? mmwEaseToSonolusEase(point.ease)
|
|
379
|
+
: SONOLUS_CONNECTOR_EASES.linear,
|
|
380
|
+
isSeparator: 0,
|
|
381
|
+
segmentKind: sonolusColorId,
|
|
382
|
+
segmentAlpha: segmentAlpha,
|
|
383
|
+
segmentLayer: 0,
|
|
384
|
+
'#TIMESCALE_GROUP': timeScaleGroupRef,
|
|
385
|
+
});
|
|
386
|
+
if (headMidpointIntermediate === null) {
|
|
387
|
+
headMidpointIntermediate = midpointIntermediate;
|
|
388
|
+
}
|
|
389
|
+
if (prevMidpointIntermediate !== null) {
|
|
390
|
+
const connectorIntermediate = createIntermediate('Connector', {
|
|
391
|
+
head: prevMidpointIntermediate,
|
|
392
|
+
tail: midpointIntermediate,
|
|
393
|
+
});
|
|
394
|
+
guideConnectors.push(connectorIntermediate);
|
|
395
|
+
prevMidpointIntermediate.data['next'] = midpointIntermediate;
|
|
396
|
+
}
|
|
397
|
+
prevMidpointIntermediate = midpointIntermediate;
|
|
398
|
+
stepIdx++;
|
|
399
|
+
}
|
|
400
|
+
if (headMidpointIntermediate && prevMidpointIntermediate) {
|
|
401
|
+
for (const conn of guideConnectors) {
|
|
402
|
+
conn.data['segmentHead'] = headMidpointIntermediate;
|
|
403
|
+
conn.data['segmentTail'] = prevMidpointIntermediate;
|
|
404
|
+
conn.data['activeHead'] = headMidpointIntermediate;
|
|
405
|
+
conn.data['activeTail'] = prevMidpointIntermediate;
|
|
406
|
+
}
|
|
407
|
+
if (hold.fadeType === 2) {
|
|
408
|
+
headMidpointIntermediate.data['segmentAlpha'] = 0;
|
|
409
|
+
}
|
|
410
|
+
else if (hold.fadeType === 0) {
|
|
411
|
+
prevMidpointIntermediate.data['segmentAlpha'] = 0;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
let prevJointIntermediate = null;
|
|
417
|
+
let prevNoteIntermediate = null;
|
|
418
|
+
let headNoteIntermediate = null;
|
|
419
|
+
let currentSegmentHead = null;
|
|
420
|
+
const queuedAttachIntermediates = [];
|
|
421
|
+
const connectors = [];
|
|
422
|
+
const pendingSegmentConnectors = [];
|
|
423
|
+
const events = [];
|
|
424
|
+
events.push({
|
|
425
|
+
tick: hold.start.tick,
|
|
426
|
+
lane: hold.start.lane,
|
|
427
|
+
width: hold.start.width,
|
|
428
|
+
type: 'start',
|
|
429
|
+
critical: hold.start.flags.critical,
|
|
430
|
+
friction: hold.start.flags.friction,
|
|
431
|
+
flick: 'none',
|
|
432
|
+
ease: hold.start.ease,
|
|
433
|
+
});
|
|
434
|
+
for (const step of hold.steps) {
|
|
435
|
+
if (step.type === 'ignored') {
|
|
436
|
+
events.push({
|
|
437
|
+
tick: step.tick,
|
|
438
|
+
lane: step.lane,
|
|
439
|
+
width: step.width,
|
|
440
|
+
type: 'attach',
|
|
441
|
+
critical: hold.start.flags.critical,
|
|
442
|
+
friction: false,
|
|
443
|
+
flick: 'none',
|
|
444
|
+
ease: step.ease,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
if (step.type === 'visible') {
|
|
449
|
+
events.push({
|
|
450
|
+
tick: step.tick,
|
|
451
|
+
lane: step.lane,
|
|
452
|
+
width: step.width,
|
|
453
|
+
type: 'tick',
|
|
454
|
+
critical: hold.start.flags.critical,
|
|
455
|
+
friction: false,
|
|
456
|
+
flick: 'none',
|
|
457
|
+
ease: step.ease,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
events.push({
|
|
462
|
+
tick: step.tick,
|
|
463
|
+
lane: step.lane,
|
|
464
|
+
width: step.width,
|
|
465
|
+
type: 'attach',
|
|
466
|
+
critical: hold.start.flags.critical,
|
|
467
|
+
friction: false,
|
|
468
|
+
flick: 'none',
|
|
469
|
+
ease: step.ease,
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
events.push({
|
|
475
|
+
tick: hold.end.tick,
|
|
476
|
+
lane: hold.end.lane,
|
|
477
|
+
width: hold.end.width,
|
|
478
|
+
type: 'end',
|
|
479
|
+
critical: hold.end.flags.critical,
|
|
480
|
+
friction: hold.end.flags.friction,
|
|
481
|
+
flick: hold.end.flickType,
|
|
482
|
+
});
|
|
483
|
+
events.sort((a, b) => a.tick - b.tick);
|
|
484
|
+
let nextHiddenTickBeat = Math.floor((hold.start.tick / TICKS_PER_BEAT) * 2 + 1) / 2;
|
|
485
|
+
for (const event of events) {
|
|
486
|
+
let isSimLineEligible = false;
|
|
487
|
+
let isAttached = false;
|
|
488
|
+
const name_parts = [];
|
|
489
|
+
if (event.type === 'start') {
|
|
490
|
+
if (hold.flags.startHidden) {
|
|
491
|
+
name_parts.push('Anchor');
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
name_parts.push(event.critical ? 'Critical' : 'Normal');
|
|
495
|
+
name_parts.push('Head');
|
|
496
|
+
name_parts.push(event.friction ? 'Trace' : 'Tap');
|
|
497
|
+
isSimLineEligible = true;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
else if (event.type === 'end') {
|
|
501
|
+
if (hold.flags.endHidden) {
|
|
502
|
+
name_parts.push('Anchor');
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
name_parts.push(event.critical ? 'Critical' : 'Normal');
|
|
506
|
+
name_parts.push('Tail');
|
|
507
|
+
if (event.flick !== 'none') {
|
|
508
|
+
name_parts.push(event.friction ? 'TraceFlick' : 'Flick');
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
name_parts.push(event.friction ? 'Trace' : 'Release');
|
|
512
|
+
}
|
|
513
|
+
isSimLineEligible = true;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
else if (event.type === 'tick') {
|
|
517
|
+
name_parts.push(event.critical ? 'Critical' : 'Normal');
|
|
518
|
+
name_parts.push('Tick');
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
isAttached = true;
|
|
522
|
+
if (event.critical) {
|
|
523
|
+
name_parts.push('Critical');
|
|
524
|
+
name_parts.push('Tick');
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
name_parts.push('TransientHiddenTick');
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
name_parts.push('Note');
|
|
531
|
+
let archetype = name_parts.join('');
|
|
532
|
+
if (hold.flags.dummy)
|
|
533
|
+
archetype = `Fake${archetype}`;
|
|
534
|
+
const data = {
|
|
535
|
+
'#BEAT': event.tick / TICKS_PER_BEAT,
|
|
536
|
+
lane: laneToSonolusLane(event.lane, event.width),
|
|
537
|
+
size: event.width,
|
|
538
|
+
isAttached: isAttached ? 1 : 0,
|
|
539
|
+
connectorEase: event.ease
|
|
540
|
+
? mmwEaseToSonolusEase(event.ease)
|
|
541
|
+
: SONOLUS_CONNECTOR_EASES.linear,
|
|
542
|
+
isSeparator: 0,
|
|
543
|
+
segmentKind: event.critical ? 2 : 1,
|
|
544
|
+
segmentAlpha: 1,
|
|
545
|
+
segmentLayer: 0,
|
|
546
|
+
'#TIMESCALE_GROUP': timeScaleGroupRef,
|
|
547
|
+
};
|
|
548
|
+
if (event.flick && event.flick !== 'none') {
|
|
549
|
+
data['direction'] = flickTypeToDirection(event.flick);
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
data['direction'] = 0;
|
|
553
|
+
}
|
|
554
|
+
const connectionIntermediate = createIntermediate(archetype, data, isSimLineEligible);
|
|
555
|
+
if (headNoteIntermediate === null) {
|
|
556
|
+
headNoteIntermediate = connectionIntermediate;
|
|
557
|
+
}
|
|
558
|
+
if (currentSegmentHead === null) {
|
|
559
|
+
currentSegmentHead = connectionIntermediate;
|
|
560
|
+
}
|
|
561
|
+
connectionIntermediate.data['activeHead'] = headNoteIntermediate;
|
|
562
|
+
if (isAttached) {
|
|
563
|
+
queuedAttachIntermediates.push(connectionIntermediate);
|
|
564
|
+
}
|
|
565
|
+
else {
|
|
566
|
+
if (prevJointIntermediate !== null) {
|
|
567
|
+
for (const attachIntermediate of queuedAttachIntermediates) {
|
|
568
|
+
attachIntermediate.data['attachHead'] = prevJointIntermediate;
|
|
569
|
+
attachIntermediate.data['attachTail'] = connectionIntermediate;
|
|
570
|
+
}
|
|
571
|
+
queuedAttachIntermediates.length = 0;
|
|
572
|
+
while (nextHiddenTickBeat + EPSILON <
|
|
573
|
+
connectionIntermediate.data['#BEAT']) {
|
|
574
|
+
createIntermediate('TransientHiddenTickNote', {
|
|
575
|
+
'#BEAT': nextHiddenTickBeat,
|
|
576
|
+
'#TIMESCALE_GROUP': timeScaleGroupRef,
|
|
577
|
+
lane: connectionIntermediate.data['lane'],
|
|
578
|
+
size: connectionIntermediate.data['size'],
|
|
579
|
+
direction: 0,
|
|
580
|
+
isAttached: 1,
|
|
581
|
+
connectorEase: SONOLUS_CONNECTOR_EASES.linear,
|
|
582
|
+
isSeparator: 0,
|
|
583
|
+
segmentKind: 1,
|
|
584
|
+
segmentAlpha: 0,
|
|
585
|
+
activeHead: headNoteIntermediate,
|
|
586
|
+
attachHead: prevJointIntermediate,
|
|
587
|
+
attachTail: connectionIntermediate,
|
|
588
|
+
});
|
|
589
|
+
nextHiddenTickBeat += 0.5;
|
|
590
|
+
}
|
|
591
|
+
const connectorIntermediate = createIntermediate('Connector', {
|
|
592
|
+
head: prevJointIntermediate,
|
|
593
|
+
tail: connectionIntermediate,
|
|
594
|
+
kind: resolveConnectorKind(event.critical, !!hold.flags.dummy),
|
|
595
|
+
});
|
|
596
|
+
connectors.push(connectorIntermediate);
|
|
597
|
+
pendingSegmentConnectors.push(connectorIntermediate);
|
|
598
|
+
}
|
|
599
|
+
prevJointIntermediate = connectionIntermediate;
|
|
600
|
+
}
|
|
601
|
+
if (prevNoteIntermediate !== null) {
|
|
602
|
+
prevNoteIntermediate.data['next'] = connectionIntermediate;
|
|
603
|
+
}
|
|
604
|
+
prevNoteIntermediate = connectionIntermediate;
|
|
605
|
+
}
|
|
606
|
+
if (headNoteIntermediate && prevJointIntermediate) {
|
|
607
|
+
if (currentSegmentHead) {
|
|
608
|
+
for (const conn of pendingSegmentConnectors) {
|
|
609
|
+
conn.data['segmentHead'] = currentSegmentHead;
|
|
610
|
+
conn.data['segmentTail'] = prevJointIntermediate;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
for (const conn of connectors) {
|
|
614
|
+
conn.data['activeHead'] = headNoteIntermediate;
|
|
615
|
+
conn.data['activeTail'] = prevJointIntermediate;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
simLineEligibleNotes.sort((a, b) => {
|
|
621
|
+
const beatA = a.data['#BEAT'];
|
|
622
|
+
const beatB = b.data['#BEAT'];
|
|
623
|
+
if (Math.abs(beatA - beatB) > EPSILON)
|
|
624
|
+
return beatA - beatB;
|
|
625
|
+
const laneA = a.data['lane'];
|
|
626
|
+
const laneB = b.data['lane'];
|
|
627
|
+
return laneA - laneB;
|
|
628
|
+
});
|
|
629
|
+
const simGroups = [];
|
|
630
|
+
let currentSimGroup = [];
|
|
631
|
+
for (const simNote of simLineEligibleNotes) {
|
|
632
|
+
if (currentSimGroup.length === 0 ||
|
|
633
|
+
Math.abs(simNote.data['#BEAT'] - currentSimGroup[0].data['#BEAT']) < 1e-2) {
|
|
634
|
+
currentSimGroup.push(simNote);
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
simGroups.push(currentSimGroup);
|
|
638
|
+
currentSimGroup = [simNote];
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
if (currentSimGroup.length > 0)
|
|
642
|
+
simGroups.push(currentSimGroup);
|
|
643
|
+
for (const simGroup of simGroups) {
|
|
644
|
+
for (let i = 0; i < simGroup.length - 1; i++) {
|
|
645
|
+
createIntermediate('SimLine', {
|
|
646
|
+
left: simGroup[i],
|
|
647
|
+
right: simGroup[i + 1],
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
const intermediateToRef = new Map();
|
|
652
|
+
let entityRefCounter = 0;
|
|
653
|
+
const getRef = (intermediateEntity) => {
|
|
654
|
+
let ref = intermediateToRef.get(intermediateEntity);
|
|
655
|
+
if (ref)
|
|
656
|
+
return ref;
|
|
657
|
+
ref = (entityRefCounter++).toString(16);
|
|
658
|
+
intermediateToRef.set(intermediateEntity, ref);
|
|
659
|
+
return ref;
|
|
660
|
+
};
|
|
661
|
+
for (const intermediateEntity of allIntermediateEntities) {
|
|
662
|
+
const entity = {
|
|
663
|
+
archetype: intermediateEntity.archetype,
|
|
664
|
+
name: getRef(intermediateEntity),
|
|
665
|
+
data: [],
|
|
666
|
+
};
|
|
667
|
+
for (const [dataName, dataValue] of Object.entries(intermediateEntity.data)) {
|
|
668
|
+
if (typeof dataValue === 'number') {
|
|
669
|
+
entity.data.push({ name: dataName, value: dataValue });
|
|
670
|
+
}
|
|
671
|
+
else if (dataValue !== undefined) {
|
|
672
|
+
entity.data.push({ name: dataName, ref: getRef(dataValue) });
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
entities.push(entity);
|
|
676
|
+
}
|
|
677
|
+
return {
|
|
678
|
+
bgmOffset: score.metadata.musicOffset / 1000,
|
|
679
|
+
entities,
|
|
680
|
+
};
|
|
681
|
+
};
|