kipphi 2.0.1 → 2.1.0
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 +1 -3
- package/basic.ts +285 -0
- package/bpm.ts +133 -14
- package/chart.ts +272 -52
- package/chartType.schema.json +584 -0
- package/chartType2.schema.json +1107 -0
- package/chartTypes.ts +70 -11
- package/easing.ts +37 -8
- package/env.ts +36 -1
- package/evaluator.ts +72 -10
- package/event.ts +97 -43
- package/index.d.ts +3055 -0
- package/index.js +5530 -0
- package/index.ts +5 -2
- package/judgeline.ts +25 -21
- package/jumparray.ts +0 -1
- package/line.ts +246 -0
- package/macro.ts +215 -0
- package/note.ts +1 -1
- package/operation/basic.ts +285 -0
- package/operation/chart.ts +21 -0
- package/operation/event.ts +511 -0
- package/operation/index.ts +6 -0
- package/operation/line.ts +304 -0
- package/operation/macro.ts +60 -0
- package/operation/note.ts +457 -0
- package/package.json +7 -1
- package/tsconfig.json +1 -1
- package/version.ts +2 -1
- package/operation.ts +0 -1378
package/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ export {default as TC} from "./time";
|
|
|
3
3
|
export * from "./chartTypes";
|
|
4
4
|
export * from "./easing";
|
|
5
5
|
export * from "./evaluator";
|
|
6
|
+
export * from "./macro"
|
|
6
7
|
export * from "./event";
|
|
7
8
|
export * from "./note";
|
|
8
9
|
export * from "./judgeline";
|
|
@@ -10,5 +11,7 @@ export * from "./bpm";
|
|
|
10
11
|
export * from "./util";
|
|
11
12
|
export * from "./chart";
|
|
12
13
|
export * from "./jumparray";
|
|
13
|
-
export * as Op from "./operation";
|
|
14
|
-
export
|
|
14
|
+
export * as Op from "./operation/index";
|
|
15
|
+
export * from "./version";
|
|
16
|
+
export { default as Environment, KPAError } from "./env";
|
|
17
|
+
export { RPEChartCompiler } from "./rpeChartCompiler";
|
package/judgeline.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Chart, JudgeLineGroup } from "./chart";
|
|
2
|
-
import { EventType, EventValueType, JudgeLineDataKPA2, NoteType, type EventDataRPELike, type EventLayerDataKPA, type JudgeLineDataKPA, type JudgeLineDataRPE, type NNListDataKPA, type NoteDataRPE, type RGB, type TimeT, type ValueTypeOfEventType } from "./chartTypes";
|
|
2
|
+
import { EventType, EventValueESType, EventValueType, JudgeLineDataKPA2, NoteType, type EventDataRPELike, type EventLayerDataKPA, type JudgeLineDataKPA, type JudgeLineDataRPE, type NNListDataKPA, type NoteDataRPE, type RGB, type TimeT, type ValueTypeOfEventType } from "./chartTypes";
|
|
3
3
|
import type { TemplateEasingLib } from "./easing";
|
|
4
4
|
import { EventEndNode, EventNode, EventNodeLike, EventNodeSequence, EventStartNode, Monotonicity, SpeedENS } from "./event";
|
|
5
5
|
import { HNList, NNList, Note, NoteNode, NNNList } from "./note";
|
|
@@ -50,13 +50,7 @@ export class JudgeLine {
|
|
|
50
50
|
father: JudgeLine;
|
|
51
51
|
children: Set<JudgeLine> = new Set();
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
moveY: number;
|
|
55
|
-
rotate: number;
|
|
56
|
-
alpha: number;
|
|
57
|
-
transformedX: number;
|
|
58
|
-
transformedY: number;
|
|
59
|
-
optimized: boolean = false;
|
|
53
|
+
|
|
60
54
|
|
|
61
55
|
zOrder: number = 0;
|
|
62
56
|
|
|
@@ -72,6 +66,9 @@ export class JudgeLine {
|
|
|
72
66
|
*/
|
|
73
67
|
// renderMatrix: Matrix;
|
|
74
68
|
|
|
69
|
+
/**
|
|
70
|
+
* 是否随父线旋转
|
|
71
|
+
*/
|
|
75
72
|
rotatesWithFather: boolean = false;
|
|
76
73
|
|
|
77
74
|
id: number;
|
|
@@ -90,7 +87,7 @@ export class JudgeLine {
|
|
|
90
87
|
line.name = data.Name;
|
|
91
88
|
chart.judgeLineGroups[data.Group].add(line);
|
|
92
89
|
line.cover = Boolean(data.isCover);
|
|
93
|
-
line.rotatesWithFather = data.rotateWithFather;
|
|
90
|
+
line.rotatesWithFather = data.rotateWithFather ?? false;
|
|
94
91
|
line.anchor = data.anchor ?? [0.5, 0.5];
|
|
95
92
|
line.texture = data.Texture || "line.png";
|
|
96
93
|
line.zOrder = data.zOrder ?? 0;
|
|
@@ -186,7 +183,8 @@ export class JudgeLine {
|
|
|
186
183
|
line.speedSequence = EventNodeSequence.mergeSequences(speedSequences) as SpeedENS;
|
|
187
184
|
line.speedSequence.updateFloorPositionAfter(line.speedSequence.head.next, timeCalculator);
|
|
188
185
|
} else {
|
|
189
|
-
line.speedSequence =
|
|
186
|
+
line.speedSequence = speedSequences[0]
|
|
187
|
+
chart.registerEventNodeSequence(EventType.speed, `#${id}.speed`, speedSequences[0]);
|
|
190
188
|
}
|
|
191
189
|
if (data.extended) {
|
|
192
190
|
if (data.extended.scaleXEvents) {
|
|
@@ -226,7 +224,7 @@ export class JudgeLine {
|
|
|
226
224
|
const line = new JudgeLine(chart)
|
|
227
225
|
line.id = id;
|
|
228
226
|
line.name = lowerCaseNameAndTexture ? (data as JudgeLineDataKPA2).name : (data as JudgeLineDataKPA).Name;
|
|
229
|
-
line.rotatesWithFather = data.rotatesWithFather;
|
|
227
|
+
line.rotatesWithFather = data.rotatesWithFather ?? false;
|
|
230
228
|
line.anchor = data.anchor ?? [0.5, 0.5];
|
|
231
229
|
line.texture = (lowerCaseNameAndTexture ? (data as JudgeLineDataKPA2).texture : (data as JudgeLineDataKPA).Texture) ?? "line.png";
|
|
232
230
|
line.cover = data.cover ?? true;
|
|
@@ -255,9 +253,11 @@ export class JudgeLine {
|
|
|
255
253
|
}
|
|
256
254
|
}
|
|
257
255
|
for (const child of data.children) {
|
|
258
|
-
|
|
256
|
+
const childLine = JudgeLine.fromKPAJSON(version, chart, child.id, child, templates, timeCalculator)
|
|
257
|
+
childLine.father = line;
|
|
258
|
+
line.children.add(childLine);
|
|
259
259
|
}
|
|
260
|
-
const unwrap = <VT>(sequence: EventNodeSequence<
|
|
260
|
+
const unwrap = <VT extends EventValueESType>(sequence: EventNodeSequence<EventValueESType>, predicate: (value: unknown) => boolean, typeStr: keyof typeof EventValueType) => {
|
|
261
261
|
const value = sequence.head.next.value
|
|
262
262
|
if (!predicate(value)) {
|
|
263
263
|
throw err.EXPECTED_TYPED_ENS(typeStr, sequence.id, value);
|
|
@@ -289,16 +289,19 @@ export class JudgeLine {
|
|
|
289
289
|
ly.speed = null;
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
|
-
let seq:
|
|
293
|
-
if (oldSequences.length >
|
|
292
|
+
let seq: SpeedENS;
|
|
293
|
+
if (oldSequences.length > 1) {
|
|
294
294
|
|
|
295
295
|
seq = EventNodeSequence.mergeSequences(
|
|
296
296
|
oldSequences
|
|
297
|
-
);
|
|
298
|
-
|
|
297
|
+
) as SpeedENS;
|
|
298
|
+
seq.updateFloorPositionAfter((seq as SpeedENS).head.next, timeCalculator);
|
|
299
|
+
} else if (oldSequences.length === 1) {
|
|
300
|
+
chart.registerEventNodeSequence(EventType.speed, `#${line.id}.speed`, oldSequences[0]);
|
|
301
|
+
seq = oldSequences[0];
|
|
302
|
+
seq.updateFloorPositionAfter(seq.head.next, timeCalculator);
|
|
299
303
|
} else {
|
|
300
|
-
seq = chart.createEventNodeSequence(EventType.speed, `#${line.id}.speed`);
|
|
301
|
-
|
|
304
|
+
seq = chart.createEventNodeSequence(EventType.speed, `#${line.id}.speed`) as SpeedENS;
|
|
302
305
|
}
|
|
303
306
|
line.speedSequence = seq as SpeedENS;
|
|
304
307
|
}
|
|
@@ -562,7 +565,7 @@ export class JudgeLine {
|
|
|
562
565
|
let startNode: EventStartNode<number> = this.speedSequence.getNodeAt(beats);
|
|
563
566
|
let range: [number, number] = [undefined, undefined];
|
|
564
567
|
// 启用遮罩时,两个Y边界都是正数,直接返回空数组
|
|
565
|
-
if (lineMonotonicity === Monotonicity.
|
|
568
|
+
if (lineMonotonicity === Monotonicity.increasing && startY >= 0 && endY > 0) {
|
|
566
569
|
|
|
567
570
|
return result;
|
|
568
571
|
}
|
|
@@ -781,7 +784,7 @@ export class JudgeLine {
|
|
|
781
784
|
return list;
|
|
782
785
|
}
|
|
783
786
|
}
|
|
784
|
-
const list = isHold ? new HNList(speed, medianYOffset, this.chart.
|
|
787
|
+
const list = isHold ? new HNList(speed, medianYOffset, this.chart.effectiveBeats) : new NNList(speed, medianYOffset, this.chart.effectiveBeats)
|
|
785
788
|
list.parentLine = this;
|
|
786
789
|
NoteNode.connect(list.head, list.tail)
|
|
787
790
|
if (initsJump) list.initJump();
|
|
@@ -843,6 +846,7 @@ export class JudgeLine {
|
|
|
843
846
|
if (this.extendedLayer.text) {
|
|
844
847
|
eventNodeSequences.add(this.extendedLayer.text);
|
|
845
848
|
}
|
|
849
|
+
eventNodeSequences.add(this.speedSequence); // 忘了这个,特此留念(
|
|
846
850
|
return {
|
|
847
851
|
group: judgeLineGroups.indexOf(this.group),
|
|
848
852
|
id: this.id,
|
package/jumparray.ts
CHANGED
|
@@ -219,7 +219,6 @@ export class JumpArray<T extends TwoDirectionNodeLike> {
|
|
|
219
219
|
}
|
|
220
220
|
let next: T | false;
|
|
221
221
|
// console.log(this)
|
|
222
|
-
// eslint-disable-next-line no-cond-assign
|
|
223
222
|
while (next = nextFn(node, beats)) {
|
|
224
223
|
node = next;
|
|
225
224
|
if (node.type === NodeType.TAIL) {
|
package/line.ts
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { Chart, JudgeLineGroup, BasicEventName, UIName } from "../chart";
|
|
2
|
+
import { ExtendedEventTypeName, RGB } from "../chartTypes";
|
|
3
|
+
import { EventNodeSequence } from "../event";
|
|
4
|
+
import { JudgeLine, ExtendedLayer } from "../judgeline";
|
|
5
|
+
import { Operation } from "./basic";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
// 有点怪异,感觉破坏了纯净性。不管了(
|
|
10
|
+
enum JudgeLinesEditorLayoutType {
|
|
11
|
+
ordered = 0b001,
|
|
12
|
+
tree = 0b010,
|
|
13
|
+
grouped = 0b100
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export class JudgeLineInheritanceChangeOperation extends Operation {
|
|
18
|
+
originalValue: JudgeLine | null;
|
|
19
|
+
updatesEditor = true;
|
|
20
|
+
static REFLOWS = JudgeLinesEditorLayoutType.tree;
|
|
21
|
+
reflows = JudgeLineInheritanceChangeOperation.REFLOWS;
|
|
22
|
+
constructor(public chart: Chart, public judgeLine: JudgeLine, public value: JudgeLine | null) {
|
|
23
|
+
super();
|
|
24
|
+
this.originalValue = judgeLine.father;
|
|
25
|
+
// 这里只会让它静默失败,外面调用的时候能够在判断一次并抛错误才是最好的
|
|
26
|
+
if (JudgeLine.checkinterdependency(judgeLine, value)) {
|
|
27
|
+
this.ineffective = true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
do() {
|
|
31
|
+
const line = this.judgeLine;
|
|
32
|
+
line.father = this.value;
|
|
33
|
+
if (this.originalValue) {
|
|
34
|
+
this.originalValue.children.delete(line);
|
|
35
|
+
} else {
|
|
36
|
+
const index = this.chart.orphanLines.indexOf(line);
|
|
37
|
+
if (index >= 0) // Impossible to be false, theoretically
|
|
38
|
+
this.chart.orphanLines.splice(index, 1)
|
|
39
|
+
}
|
|
40
|
+
if (this.value) {
|
|
41
|
+
this.value.children.add(line);
|
|
42
|
+
} else {
|
|
43
|
+
this.chart.orphanLines.push(line);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
undo() {
|
|
47
|
+
const line = this.judgeLine;
|
|
48
|
+
line.father = this.originalValue;
|
|
49
|
+
if (this.originalValue) {
|
|
50
|
+
this.originalValue.children.add(line);
|
|
51
|
+
} else {
|
|
52
|
+
this.chart.orphanLines.push(line);
|
|
53
|
+
}
|
|
54
|
+
if (this.value) {
|
|
55
|
+
this.value.children.delete(line);
|
|
56
|
+
} else {
|
|
57
|
+
const index = this.chart.orphanLines.indexOf(line);
|
|
58
|
+
if (index >= 0) // Impossible to be false, theoretically
|
|
59
|
+
this.chart.orphanLines.splice(index, 1)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export class JudgeLineRenameOperation extends Operation {
|
|
65
|
+
updatesEditor = true;
|
|
66
|
+
originalValue: string;
|
|
67
|
+
constructor(public judgeLine: JudgeLine, public value: string) {
|
|
68
|
+
super();
|
|
69
|
+
this.originalValue = judgeLine.name;
|
|
70
|
+
}
|
|
71
|
+
do() {
|
|
72
|
+
this.judgeLine.name = this.value;
|
|
73
|
+
}
|
|
74
|
+
undo() {
|
|
75
|
+
this.judgeLine.name = this.originalValue;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
type JudgeLinePropName = "name" | "rotatesWithFather" | "anchor" | "texture" | "cover" | "zOrder";
|
|
80
|
+
|
|
81
|
+
export class JudgeLinePropChangeOperation<T extends JudgeLinePropName> extends Operation {
|
|
82
|
+
updatesEditor = true;
|
|
83
|
+
originalValue: JudgeLine[T];
|
|
84
|
+
constructor(public judgeLine: JudgeLine, public field: T, public value: JudgeLine[T]) {
|
|
85
|
+
super();
|
|
86
|
+
this.originalValue = judgeLine[field];
|
|
87
|
+
}
|
|
88
|
+
do() {
|
|
89
|
+
this.judgeLine[this.field] = this.value;
|
|
90
|
+
}
|
|
91
|
+
undo() {
|
|
92
|
+
this.judgeLine[this.field] = this.originalValue;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export class JudgeLineRegroupOperation extends Operation {
|
|
97
|
+
updatesEditor = true;
|
|
98
|
+
reflows = JudgeLinesEditorLayoutType.grouped;
|
|
99
|
+
originalValue: JudgeLineGroup;
|
|
100
|
+
constructor(public judgeLine: JudgeLine, public value: JudgeLineGroup) {
|
|
101
|
+
super();
|
|
102
|
+
this.originalValue = judgeLine.group;
|
|
103
|
+
}
|
|
104
|
+
do() {
|
|
105
|
+
this.judgeLine.group = this.value;
|
|
106
|
+
this.value.add(this.judgeLine);
|
|
107
|
+
this.originalValue.remove(this.judgeLine);
|
|
108
|
+
}
|
|
109
|
+
undo() {
|
|
110
|
+
this.judgeLine.group = this.originalValue;
|
|
111
|
+
this.originalValue.add(this.judgeLine);
|
|
112
|
+
this.value.remove(this.judgeLine);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export class JudgeLineCreateOperation extends Operation {
|
|
117
|
+
reflows = JudgeLinesEditorLayoutType.grouped | JudgeLinesEditorLayoutType.tree | JudgeLinesEditorLayoutType.ordered;
|
|
118
|
+
// 之前把=写成了:半天不知道咋错了
|
|
119
|
+
constructor(public chart: Chart, public judgeLine: JudgeLine) {
|
|
120
|
+
super();
|
|
121
|
+
}
|
|
122
|
+
do() {
|
|
123
|
+
const id = this.chart.judgeLines.length;
|
|
124
|
+
this.judgeLine.id = id;
|
|
125
|
+
this.chart.judgeLines.push(this.judgeLine);
|
|
126
|
+
this.chart.orphanLines.push(this.judgeLine);
|
|
127
|
+
this.chart.judgeLineGroups[0].add(this.judgeLine);
|
|
128
|
+
}
|
|
129
|
+
undo() {
|
|
130
|
+
this.chart.judgeLineGroups[0].remove(this.judgeLine);
|
|
131
|
+
this.chart.judgeLines.splice(this.chart.judgeLines.indexOf(this.judgeLine), 1);
|
|
132
|
+
this.chart.orphanLines.splice(this.chart.orphanLines.indexOf(this.judgeLine), 1);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export class JudgeLineDeleteOperation extends Operation {
|
|
137
|
+
readonly originalGroup: JudgeLineGroup;
|
|
138
|
+
constructor(public chart: Chart, public judgeLine: JudgeLine) {
|
|
139
|
+
super();
|
|
140
|
+
if (!this.chart.judgeLines.includes(this.judgeLine)) {
|
|
141
|
+
this.ineffective = true;
|
|
142
|
+
}
|
|
143
|
+
this.originalGroup = judgeLine.group;
|
|
144
|
+
}
|
|
145
|
+
do() {
|
|
146
|
+
this.chart.judgeLines.splice(this.chart.judgeLines.indexOf(this.judgeLine), 1);
|
|
147
|
+
if (this.chart.orphanLines.includes(this.judgeLine)) {
|
|
148
|
+
this.chart.orphanLines.splice(this.chart.orphanLines.indexOf(this.judgeLine), 1);
|
|
149
|
+
}
|
|
150
|
+
this.originalGroup.remove(this.judgeLine);
|
|
151
|
+
}
|
|
152
|
+
undo() {
|
|
153
|
+
this.chart.judgeLines.push(this.judgeLine);
|
|
154
|
+
this.chart.orphanLines.push(this.judgeLine);
|
|
155
|
+
this.originalGroup.add(this.judgeLine);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
export class JudgeLineENSChangeOperation extends Operation {
|
|
162
|
+
originalValue: EventNodeSequence;
|
|
163
|
+
constructor(public judgeLine: JudgeLine, public layerId: number, public typeStr: BasicEventName, public value: EventNodeSequence) {
|
|
164
|
+
super();
|
|
165
|
+
this.originalValue = judgeLine.eventLayers[layerId][typeStr];
|
|
166
|
+
}
|
|
167
|
+
do() {
|
|
168
|
+
this.judgeLine.eventLayers[this.layerId][this.typeStr] = this.value;
|
|
169
|
+
}
|
|
170
|
+
undo() {
|
|
171
|
+
this.judgeLine.eventLayers[this.layerId][this.typeStr] = this.originalValue;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
export type ENSOfTypeName<T extends ExtendedEventTypeName> = {
|
|
177
|
+
"scaleX": EventNodeSequence<number>,
|
|
178
|
+
"scaleY": EventNodeSequence<number>
|
|
179
|
+
"text": EventNodeSequence<string>,
|
|
180
|
+
"color": EventNodeSequence<RGB>
|
|
181
|
+
}[T]
|
|
182
|
+
export class JudgeLineExtendENSChangeOperation<T extends ExtendedEventTypeName> extends Operation {
|
|
183
|
+
originalValue: ENSOfTypeName<T>;
|
|
184
|
+
constructor(public judgeLine: JudgeLine, public typeStr: T, public value: ENSOfTypeName<T> | null) {
|
|
185
|
+
super();
|
|
186
|
+
this.originalValue = judgeLine.extendedLayer[typeStr satisfies keyof ExtendedLayer] as ENSOfTypeName<T>;
|
|
187
|
+
}
|
|
188
|
+
do() {
|
|
189
|
+
this.judgeLine.extendedLayer[this.typeStr] = this.value
|
|
190
|
+
}
|
|
191
|
+
undo() {
|
|
192
|
+
this.judgeLine.extendedLayer[this.typeStr] = this.originalValue
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export class UIAttachOperation extends Operation {
|
|
198
|
+
updatesEditor = true;
|
|
199
|
+
constructor(public chart: Chart, public judgeLine: JudgeLine, public ui: UIName) {
|
|
200
|
+
super();
|
|
201
|
+
}
|
|
202
|
+
do() {
|
|
203
|
+
this.chart.attachUIToLine(this.ui, this.judgeLine);
|
|
204
|
+
}
|
|
205
|
+
undo() {
|
|
206
|
+
this.chart.detachUI(this.ui);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export class UIDetachOperation extends Operation {
|
|
211
|
+
updatesEditor = true;
|
|
212
|
+
judgeLine: JudgeLine;
|
|
213
|
+
constructor(public chart: Chart, public ui: UIName) {
|
|
214
|
+
super();
|
|
215
|
+
if (chart[`${ui}Attach` satisfies keyof Chart]) {
|
|
216
|
+
this.judgeLine = chart[`${ui}Attach` satisfies keyof Chart];
|
|
217
|
+
} else {
|
|
218
|
+
this.ineffective = true;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
do() {
|
|
222
|
+
this.chart.detachUI(this.ui);
|
|
223
|
+
}
|
|
224
|
+
undo() {
|
|
225
|
+
this.chart.attachUIToLine(this.ui, this.judgeLine);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export class JudgeLineDetachAllUIOperation extends Operation {
|
|
230
|
+
updatesEditor = true;
|
|
231
|
+
uinames: UIName[];
|
|
232
|
+
constructor(public chart: Chart, public judgeLine: JudgeLine) {
|
|
233
|
+
super();
|
|
234
|
+
this.uinames = chart.queryJudgeLineUI(this.judgeLine);
|
|
235
|
+
}
|
|
236
|
+
do() {
|
|
237
|
+
for (const ui of this.uinames) {
|
|
238
|
+
this.chart.detachUI(ui);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
undo() {
|
|
242
|
+
for (const ui of this.uinames) {
|
|
243
|
+
this.chart.attachUIToLine(ui, this.judgeLine);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
package/macro.ts
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import type { Chart } from "./chart";
|
|
2
|
+
import type { EventValueESType, MacroData, MacroEvaluatorBodyData, MacroLink, MacroTimeBodyData, MacroValueBodyData, RGB, TimeT } from "./chartTypes";
|
|
3
|
+
import { err, ERROR_IDS, KPAError } from "./env";
|
|
4
|
+
import { MacroEvaluator } from "./evaluator";
|
|
5
|
+
import type { EventNode, EventStartNode } from "./event";
|
|
6
|
+
import type { JudgeLine } from "./judgeline";
|
|
7
|
+
import { Note } from "./note";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const getLine = (node: EventStartNode<EventValueESType>, chart: Chart) => {
|
|
11
|
+
const canBeId = node.parentSeq.id.match(/#(\d+)/);
|
|
12
|
+
if (!canBeId) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return chart.judgeLines[parseInt(canBeId[1])]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
// 添加表达式宏,通过@表示。如@line.id表示判定线ID,会在构造函数中被替换为实际值
|
|
20
|
+
export const EVENT_MACROS = {
|
|
21
|
+
"line.id": (node: EventStartNode<EventValueESType>, chart: Chart) => getLine(node, chart)?.id,
|
|
22
|
+
"line.name": (node: EventStartNode<EventValueESType>, chart: Chart) => getLine(node, chart)?.name,
|
|
23
|
+
"line.group": (node: EventStartNode<EventValueESType>, chart: Chart) => getLine(node, chart)?.group,
|
|
24
|
+
"node.time": (node: EventStartNode<EventValueESType>) => node.time,
|
|
25
|
+
"node.value": (node: EventStartNode<EventValueESType>) => node.value,
|
|
26
|
+
"seq.id": (node: EventStartNode<EventValueESType>) => node.parentSeq.id,
|
|
27
|
+
"seq.type": (node: EventStartNode<EventValueESType>) => node.parentSeq.type
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type Macroable = number | string | TimeT | RGB | boolean;
|
|
31
|
+
export type Macroee = EventNode<EventValueESType> | Note;
|
|
32
|
+
|
|
33
|
+
export abstract class Macro<T extends Macroable, C extends Macroee, P extends Macroee> {
|
|
34
|
+
public readonly consumers: Map<C, number> = new Map();
|
|
35
|
+
/**
|
|
36
|
+
* **原形节点**
|
|
37
|
+
*
|
|
38
|
+
* 作为参数,向其他节点派生数值。
|
|
39
|
+
*
|
|
40
|
+
* 需要parametric为true。
|
|
41
|
+
*
|
|
42
|
+
* 如:若节点A存在于这个数组,下标为1:
|
|
43
|
+
*
|
|
44
|
+
* 节点B设有数值宏,即这个宏,宏表达式: @proto.value + 2。
|
|
45
|
+
*
|
|
46
|
+
* 那么评估B的宏时可以调用节点A的数值。
|
|
47
|
+
*
|
|
48
|
+
* 在转储时,是节点存储宏的ID,而不是宏去找节点,因为节点并没有全谱面编号系统。
|
|
49
|
+
*/
|
|
50
|
+
public readonly protoNodes: P[] = [];
|
|
51
|
+
/**
|
|
52
|
+
*
|
|
53
|
+
* @param macro
|
|
54
|
+
* @param id
|
|
55
|
+
* @param parametric 是否为参数宏,即是否可以把一个节点作为参数。
|
|
56
|
+
*/
|
|
57
|
+
constructor(public macro: string, public id: string, public readonly parametric: boolean) {
|
|
58
|
+
}
|
|
59
|
+
abstract eval(consumer: C, chart: Chart): T;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export abstract class EventMacro<T extends Macroable> extends Macro<T, EventNode<EventValueESType>, EventNode<EventValueESType>> {
|
|
63
|
+
eval(node: EventNode<EventValueESType>, chart: Chart): T {
|
|
64
|
+
let jsExpr = this.parametric ? this.macro.replace(/@proto\.(value|time)/, (k) => {
|
|
65
|
+
return JSON.stringify(this.protoNodes[this.consumers.get(node)!][k]) + " "
|
|
66
|
+
}): this.macro;
|
|
67
|
+
jsExpr = this.macro.replace(/@([a-z]+\.[a-z])/, (k) => {
|
|
68
|
+
return JSON.stringify(EVENT_MACROS[k](node, chart)) + " "
|
|
69
|
+
});
|
|
70
|
+
return new Function("return " + jsExpr)() as T;
|
|
71
|
+
}
|
|
72
|
+
checkSyntax(): KPAError<ERROR_IDS.PROTO_PRESENT_IN_NONPARAMETRIC | ERROR_IDS.UNKNOWN_MACRO_EXPRESSION | ERROR_IDS.JAVASCRIPT_SYNTAX_ERROR> | null {
|
|
73
|
+
let jsExpr: string;
|
|
74
|
+
if (this.parametric) {
|
|
75
|
+
jsExpr = this.macro.replace(/@proto\.(value|time)/, (k) => {
|
|
76
|
+
return '"aaa" '
|
|
77
|
+
});
|
|
78
|
+
} else {
|
|
79
|
+
if (/@proto/.test(this.macro)) {
|
|
80
|
+
return err.PROTO_PRESENT_IN_NONPARAMETRIC(this.id);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
jsExpr = this.macro.replace(/@([a-z]+\.[a-z])/, (k) => {
|
|
85
|
+
if (!(k in EVENT_MACROS)) {
|
|
86
|
+
throw err.UNKNOWN_MACRO_EXPRESSION(k, this.id);
|
|
87
|
+
}
|
|
88
|
+
return '"aaa" '
|
|
89
|
+
});
|
|
90
|
+
} catch (e) {
|
|
91
|
+
if (e instanceof KPAError) {
|
|
92
|
+
return e;
|
|
93
|
+
} else {
|
|
94
|
+
throw e;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
new Function("return " + jsExpr); // 仅检查语法,不执行
|
|
99
|
+
} catch (e) {
|
|
100
|
+
return err.JAVASCRIPT_SYNTAX_ERROR(e, this.id);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
bindNode(node: EventNode<EventValueESType>, macroData: MacroData, pos?: string) {
|
|
104
|
+
if (this.parametric) {
|
|
105
|
+
if (typeof macroData === "string") {
|
|
106
|
+
throw err.PARAMETRIC_MACRO_REQUIRES_PROTO_KEY(pos)
|
|
107
|
+
}
|
|
108
|
+
this.consumers.set(node, macroData[1]);
|
|
109
|
+
} else {
|
|
110
|
+
if (Array.isArray(macroData)) {
|
|
111
|
+
throw err.MACRO_NOT_PARAMETRIC(this.id, pos);
|
|
112
|
+
}
|
|
113
|
+
this.consumers.set(node, -1);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
linkProtoNode(node: EventNode<EventValueESType>, id: number): void {
|
|
117
|
+
this.protoNodes[id] = node;
|
|
118
|
+
node.linkedMacros.add(this);
|
|
119
|
+
}
|
|
120
|
+
dumpContent(): MacroValueBodyData {
|
|
121
|
+
return {
|
|
122
|
+
id: this.id,
|
|
123
|
+
macro: this.macro,
|
|
124
|
+
parametric: this.parametric ? true : undefined
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* 为单个节点导出宏名称和参数(若有)
|
|
129
|
+
* @param node
|
|
130
|
+
* @example
|
|
131
|
+
* return {
|
|
132
|
+
* start: node.value,
|
|
133
|
+
* end: node.next.value,
|
|
134
|
+
* macroStart: node.valueMacro.dumpForNode(node),
|
|
135
|
+
* macroEnd: node.valueMacro.dumpForNode(node.next),
|
|
136
|
+
* ...
|
|
137
|
+
* } satisfies EvenDataKPA2
|
|
138
|
+
*
|
|
139
|
+
*/
|
|
140
|
+
dumpForNode(node: EventNode<EventValueESType>): MacroData {
|
|
141
|
+
if (this.parametric) {
|
|
142
|
+
return [this.id, this.consumers.get(node)!]
|
|
143
|
+
} else {
|
|
144
|
+
return this.id
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
abstract dumpLinkForNode(node: EventNode<EventValueESType>): MacroLink;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export class EventMacroValue extends EventMacro<EventValueESType> {
|
|
151
|
+
dumpLinkForNode(node: EventNode<EventValueESType>): MacroLink {
|
|
152
|
+
return [`value:${this.id}`, this.protoNodes.indexOf(node)];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export class EventMacroTime extends EventMacro<TimeT> {
|
|
157
|
+
dumpLinkForNode(node: EventNode<EventValueESType>): MacroLink {
|
|
158
|
+
return [`time:${this.id}`, this.protoNodes.indexOf(node)];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export class MacroLib {
|
|
163
|
+
timeMacros: Map<string, EventMacroTime> = new Map();
|
|
164
|
+
valueMacros: Map<string, EventMacroValue> = new Map();
|
|
165
|
+
macroEvaluators: Map<string, MacroEvaluator<EventValueESType>> = new Map();
|
|
166
|
+
dumpTimeMacros(): MacroTimeBodyData[] {
|
|
167
|
+
const arr: MacroTimeBodyData[] = [];
|
|
168
|
+
for (const [_, macro] of this.timeMacros) {
|
|
169
|
+
arr.push(macro.dumpContent());
|
|
170
|
+
}
|
|
171
|
+
return arr;
|
|
172
|
+
}
|
|
173
|
+
readTimeMacros(data: MacroTimeBodyData[]) {
|
|
174
|
+
const len = data.length;
|
|
175
|
+
for (let i = 0; i < len; i++) {
|
|
176
|
+
const datum = data[i];
|
|
177
|
+
const macro = new EventMacroTime(datum.macro, datum.id, datum.parametric);
|
|
178
|
+
this.timeMacros.set(datum.id, macro);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
dumpValueMacros(): MacroValueBodyData[] {
|
|
183
|
+
const arr: MacroValueBodyData[] = [];
|
|
184
|
+
for (const [_, macro] of this.valueMacros) {
|
|
185
|
+
arr.push(macro.dumpContent());
|
|
186
|
+
}
|
|
187
|
+
return arr;
|
|
188
|
+
}
|
|
189
|
+
readValueMacros(data: MacroValueBodyData[]) {
|
|
190
|
+
const len = data.length;
|
|
191
|
+
for (let i = 0; i < len; i++) {
|
|
192
|
+
const datum = data[i];
|
|
193
|
+
const macro = new EventMacroValue(datum.macro, datum.id, datum.parametric);
|
|
194
|
+
this.valueMacros.set(datum.id, macro);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
dumpMacroEvaluators(): MacroEvaluatorBodyData[] {
|
|
199
|
+
const arr: MacroEvaluatorBodyData[] = [];
|
|
200
|
+
for (const [_, ev] of this.macroEvaluators) {
|
|
201
|
+
arr.push({
|
|
202
|
+
id: ev.id,
|
|
203
|
+
macro: ev.expression
|
|
204
|
+
})
|
|
205
|
+
}
|
|
206
|
+
return arr;
|
|
207
|
+
}
|
|
208
|
+
readMacroEvaluators(data: MacroEvaluatorBodyData[]): void {
|
|
209
|
+
const len = data.length;
|
|
210
|
+
for (let i = 0; i < len; i++) {
|
|
211
|
+
const datum = data[i];
|
|
212
|
+
this.macroEvaluators.set(datum.id, new MacroEvaluator(datum.macro, datum.id));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
package/note.ts
CHANGED
|
@@ -185,7 +185,7 @@ export class Note {
|
|
|
185
185
|
size: this.size,
|
|
186
186
|
startTime: this.startTime,
|
|
187
187
|
type: this.type,
|
|
188
|
-
visibleBeats: this.visibleBeats,
|
|
188
|
+
visibleBeats: this.visibleBeats === Infinity ? undefined : this.visibleBeats, // 无穷不要保存,节约空间
|
|
189
189
|
yOffset: this.yOffset / this.speed,
|
|
190
190
|
/** 新KPAJSON认为YOffset就应该是个绝对的值,不受速度影响 */
|
|
191
191
|
/** 但是有历史包袱,所以加字段 */
|