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/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 { default as Environment } from "./env";
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
- moveX: number;
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 = chart.createEventNodeSequence(EventType.speed, `#${id}.speed`) as SpeedENS;
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
- line.children.add(JudgeLine.fromKPAJSON(version, chart, child.id, child, templates, timeCalculator));
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<unknown>, predicate: (value: unknown) => boolean, typeStr: keyof typeof EventValueType) => {
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: EventNodeSequence<number>;
293
- if (oldSequences.length > 0) {
292
+ let seq: SpeedENS;
293
+ if (oldSequences.length > 1) {
294
294
 
295
295
  seq = EventNodeSequence.mergeSequences(
296
296
  oldSequences
297
- );
298
- (seq as SpeedENS).updateFloorPositionAfter((seq as SpeedENS).head.next, timeCalculator);
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.decreasing && startY >= 0 && endY > 0) {
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.timeCalculator.duration) : new NNList(speed, medianYOffset, this.chart.timeCalculator.duration)
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
  /** 但是有历史包袱,所以加字段 */