kipphi 2.0.0 → 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 +332 -0
- package/chart.ts +418 -106
- package/chartType.schema.json +584 -0
- package/chartType2.schema.json +1107 -0
- package/chartTypes.ts +131 -30
- package/easing.ts +125 -90
- package/env.ts +208 -0
- package/evaluator.ts +106 -20
- package/event.ts +357 -255
- package/index.d.ts +3055 -0
- package/index.js +5530 -0
- package/index.ts +17 -11
- package/judgeline.ts +395 -94
- package/jumparray.ts +10 -11
- package/line.ts +246 -0
- package/macro.ts +215 -0
- package/note.ts +32 -55
- 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/rpeChartCompiler.ts +133 -98
- package/time.ts +35 -223
- package/tsconfig.json +2 -3
- package/util.ts +21 -1
- package/version.ts +2 -1
package/jumparray.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { NodeType } from "./util";
|
|
2
|
+
import Env from "./env";
|
|
2
3
|
|
|
3
4
|
// type EndBeats = number;
|
|
4
|
-
const MIN_LENGTH = 128
|
|
5
|
-
const MAX_LENGTH = 1024
|
|
6
|
-
const MINOR_PARTS = 16;
|
|
7
5
|
/// #declaration:global
|
|
8
6
|
|
|
9
7
|
type EndNextFn<T extends TwoDirectionNodeLike> = (node: T) => [endBeats: number, next: T];
|
|
@@ -23,6 +21,7 @@ export class JumpArray<T extends TwoDirectionNodeLike> {
|
|
|
23
21
|
averageBeats: number;
|
|
24
22
|
effectiveBeats: number;
|
|
25
23
|
goPrev: (node: T) => T;
|
|
24
|
+
readonly MINOR_SCALE_COUNT: number = Env.JUMPARRAY_MINOR_SCALE_COUNT;
|
|
26
25
|
|
|
27
26
|
/**
|
|
28
27
|
*
|
|
@@ -46,7 +45,7 @@ export class JumpArray<T extends TwoDirectionNodeLike> {
|
|
|
46
45
|
this.header = head;
|
|
47
46
|
this.tailer = tail;
|
|
48
47
|
// const originalListLength = this.listLength
|
|
49
|
-
const listLength: number = Math.max(
|
|
48
|
+
const listLength: number = Math.max(Env.JUMPARRAY_MIN_LENGTH, Math.min(originalListLength * 4, Env.JUMPARRAY_MAX_LENGTH));
|
|
50
49
|
const averageBeats: number = Math.pow(2, Math.ceil(Math.log2(effectiveBeats / listLength)));
|
|
51
50
|
const exactLength: number = Math.ceil(effectiveBeats / averageBeats);
|
|
52
51
|
// console.log(exactLength, listLength, averageBeats, exactLength)
|
|
@@ -91,6 +90,7 @@ export class JumpArray<T extends TwoDirectionNodeLike> {
|
|
|
91
90
|
updateRange(firstNode: T, lastNode: T) {
|
|
92
91
|
const {endNextFn, effectiveBeats, resolveLastNode} = this;
|
|
93
92
|
lastNode = resolveLastNode(lastNode);
|
|
93
|
+
const MINOR_PARTS = this.MINOR_SCALE_COUNT;
|
|
94
94
|
// console.log(firstNode, lastNode)
|
|
95
95
|
/**
|
|
96
96
|
*
|
|
@@ -125,16 +125,13 @@ export class JumpArray<T extends TwoDirectionNodeLike> {
|
|
|
125
125
|
if (Array.isArray(jumpArray[jumpIndex])) {
|
|
126
126
|
fillMinor(previousEndTime, endTime)
|
|
127
127
|
} else {
|
|
128
|
-
try {
|
|
129
|
-
// console.log(jumpIndex, currentNode)
|
|
130
128
|
jumpArray[jumpIndex] = currentNode;
|
|
131
|
-
} catch (E) {console.log(jumpIndex, jumpArray);debugger}
|
|
132
129
|
}
|
|
133
130
|
jumpIndex++;
|
|
134
131
|
}
|
|
135
132
|
const currentJumpBeats: number = jumpIndex * averageBeats // 放错了
|
|
136
133
|
if (endTime > currentJumpBeats) {
|
|
137
|
-
|
|
134
|
+
const minor = jumpArray[jumpIndex];
|
|
138
135
|
if (!Array.isArray(minor)) {
|
|
139
136
|
jumpArray[jumpIndex] = new Array(MINOR_PARTS);
|
|
140
137
|
}
|
|
@@ -175,8 +172,9 @@ export class JumpArray<T extends TwoDirectionNodeLike> {
|
|
|
175
172
|
const jumpAverageBeats = this.averageBeats;
|
|
176
173
|
const jumpPos = Math.floor(beats / jumpAverageBeats);
|
|
177
174
|
const rest = beats - jumpPos * jumpAverageBeats;
|
|
175
|
+
const MINOR_PARTS = this.MINOR_SCALE_COUNT;
|
|
178
176
|
for (let i = jumpPos; i >= 0; i--) {
|
|
179
|
-
|
|
177
|
+
const canBeNodeOrArray: T | T[] = this.array[i];
|
|
180
178
|
if (Array.isArray(canBeNodeOrArray)) {
|
|
181
179
|
const minorIndex = Math.floor(rest / (jumpAverageBeats / MINOR_PARTS)) - 1;
|
|
182
180
|
for (let j = minorIndex; j >= 0; j--) {
|
|
@@ -202,11 +200,12 @@ export class JumpArray<T extends TwoDirectionNodeLike> {
|
|
|
202
200
|
if (beats >= this.effectiveBeats) {
|
|
203
201
|
return this.tailer;
|
|
204
202
|
}
|
|
203
|
+
const MINOR_PARTS = this.MINOR_SCALE_COUNT;
|
|
205
204
|
const jumpAverageBeats = this.averageBeats;
|
|
206
205
|
const jumpPos = Math.floor(beats / jumpAverageBeats);
|
|
207
206
|
const rest = beats - jumpPos * jumpAverageBeats;
|
|
208
207
|
const nextFn = this.nextFn;
|
|
209
|
-
|
|
208
|
+
const canBeNodeOrArray: T | T[] = this.array[jumpPos]
|
|
210
209
|
let node: T = Array.isArray(canBeNodeOrArray)
|
|
211
210
|
? canBeNodeOrArray[Math.floor(rest / (jumpAverageBeats / MINOR_PARTS))]
|
|
212
211
|
: canBeNodeOrArray;
|
|
@@ -216,7 +215,7 @@ export class JumpArray<T extends TwoDirectionNodeLike> {
|
|
|
216
215
|
// console.log(this, node, jumpPos, beats)
|
|
217
216
|
if (!node) {
|
|
218
217
|
console.warn("No node:", node, beats)
|
|
219
|
-
|
|
218
|
+
throw new Error();
|
|
220
219
|
}
|
|
221
220
|
let next: T | false;
|
|
222
221
|
// console.log(this)
|
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
|
+
}
|