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/chart.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
import { VERSION } from "./version";
|
|
1
|
+
import { SCHEMA, VERSION } from "./version";
|
|
3
2
|
|
|
4
3
|
import {
|
|
5
|
-
TimeCalculator
|
|
4
|
+
TimeCalculator
|
|
5
|
+
} from "./bpm";
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
BezierEasing,
|
|
@@ -56,10 +56,16 @@ import {
|
|
|
56
56
|
type EventNodeSequenceDataKPA2,
|
|
57
57
|
type ChartDataKPA2,
|
|
58
58
|
type EventDataKPA2,
|
|
59
|
-
InterpreteAs
|
|
59
|
+
InterpreteAs,
|
|
60
|
+
MacroEvaluatorBodyData,
|
|
61
|
+
MacroEvaluatorDataKPA2,
|
|
62
|
+
FinalEventStartNodeDataKPA2,
|
|
63
|
+
MacroData,
|
|
64
|
+
MacroLink
|
|
60
65
|
} from "./chartTypes";
|
|
61
66
|
import { ColorEasedEvaluator, Evaluator, ExpressionEvaluator, NumericEasedEvaluator, TextEasedEvaluator, type EasedEvaluatorOfType } from "./evaluator";
|
|
62
67
|
import { err, ERROR_IDS, KPAError } from "./env";
|
|
68
|
+
import { MacroLib, EventMacroTime, EventMacroValue, EventMacro } from "./macro";
|
|
63
69
|
|
|
64
70
|
/// #declaration:global
|
|
65
71
|
|
|
@@ -72,51 +78,95 @@ export type BasicEventName = "moveX" | "moveY" | "rotate" | "alpha" | "speed";
|
|
|
72
78
|
|
|
73
79
|
export type UIName = "combo" | "combonumber" | "score" | "pause" | "bar" | "name" | "level"
|
|
74
80
|
|
|
81
|
+
/**
|
|
82
|
+
* 表示一张谱面的核心数据结构
|
|
83
|
+
*
|
|
84
|
+
* 包含了谱面的所有元素:判定线、音符、事件序列等信息
|
|
85
|
+
*/
|
|
75
86
|
export class Chart {
|
|
87
|
+
/** 谱面中所有的判定线列表 */
|
|
76
88
|
judgeLines: JudgeLine[] = [];
|
|
77
|
-
|
|
89
|
+
/** 时间计算器,用于处理BPM变化和时间转换 */
|
|
90
|
+
readonly timeCalculator = new TimeCalculator();
|
|
91
|
+
/** 无父级的根判定线列表 */
|
|
78
92
|
orphanLines: JudgeLine[] = [];
|
|
79
|
-
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
/** 谱面名称 */
|
|
80
97
|
name: string = "unknown";
|
|
98
|
+
/** 谱面难度等级 */
|
|
81
99
|
level: string = "unknown";
|
|
100
|
+
/** 曲师信息 */
|
|
82
101
|
composer: string = "unknown";
|
|
102
|
+
/** 谱师信息 */
|
|
83
103
|
charter: string = "unknown";
|
|
104
|
+
/** 插画师信息 */
|
|
84
105
|
illustrator: string = "unknown";
|
|
106
|
+
|
|
107
|
+
/** 谱面偏移时间(秒) */
|
|
85
108
|
offset: number = 0;
|
|
86
109
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
110
|
+
/** 模板缓动库,用于管理和复用缓动函数 */
|
|
111
|
+
readonly templateEasingLib = new TemplateEasingLib(EventNodeSequence.newSeq<EventType.easing>, ExpressionEvaluator);
|
|
112
|
+
readonly macroLib = new MacroLib();
|
|
113
|
+
|
|
114
|
+
/** 事件序列映射表,通过ID索引事件序列 */
|
|
115
|
+
readonly sequenceMap = new Map<string, EventNodeSequence<EventValueESType>>();
|
|
116
|
+
/** 有效节拍数(基于谱面持续时间计算得出) */
|
|
90
117
|
effectiveBeats: number;
|
|
118
|
+
/** 音符节点列表,用于管理谱面上的所有音符 */
|
|
91
119
|
nnnList: NNNList;
|
|
92
|
-
/**
|
|
120
|
+
/** 判定线组列表,用于组织和分类判定线 */
|
|
93
121
|
judgeLineGroups: JudgeLineGroup[] = [];
|
|
122
|
+
/** 谱面持续时间(秒) */
|
|
94
123
|
duration: number;
|
|
95
124
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
125
|
+
/** 谱面制作所用时间(以秒计) */
|
|
126
|
+
chartingSeconds: number;
|
|
127
|
+
/** RPE格式的谱面制作时间(以秒计) */
|
|
128
|
+
rpeChartingSeconds: number;
|
|
99
129
|
|
|
100
130
|
|
|
131
|
+
/** 标记谱面是否已被修改 */
|
|
101
132
|
modified: boolean = false;
|
|
133
|
+
/** 谱面最大连击数 */
|
|
102
134
|
maxCombo: number = 0;
|
|
103
135
|
|
|
104
136
|
|
|
137
|
+
/** 暂停按钮绑定的判定线 */
|
|
105
138
|
pauseAttach: JudgeLine | null = null;
|
|
139
|
+
/** 连击数字绑定的判定线 */
|
|
106
140
|
combonumberAttach: JudgeLine | null = null;
|
|
141
|
+
/** 连击标识绑定的判定线 */
|
|
107
142
|
comboAttach: JudgeLine | null = null;
|
|
143
|
+
/** 进度条绑定的判定线 */
|
|
108
144
|
barAttach: JudgeLine | null = null;
|
|
145
|
+
/** 分数显示绑定的判定线 */
|
|
109
146
|
scoreAttach: JudgeLine | null = null;
|
|
147
|
+
/** 歌曲名称显示绑定的判定线 */
|
|
110
148
|
nameAttach: JudgeLine | null = null;
|
|
149
|
+
/** 难度等级显示绑定的判定线 */
|
|
111
150
|
levelAttach: JudgeLine | null = null;
|
|
112
151
|
|
|
113
152
|
constructor() {}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 获取有效节拍数
|
|
156
|
+
* @returns 基于谱面持续时间计算的有效节拍数
|
|
157
|
+
*/
|
|
114
158
|
getEffectiveBeats() {
|
|
115
159
|
const effectiveBeats = this.timeCalculator.secondsToBeats(this.duration)
|
|
116
|
-
console.log(effectiveBeats)
|
|
117
160
|
this.effectiveBeats = effectiveBeats
|
|
118
161
|
return this.effectiveBeats
|
|
119
162
|
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* 从RPE格式的JSON数据创建谱面对象
|
|
166
|
+
* @param data RPE格式的谱面数据
|
|
167
|
+
* @param duration 谱面持续时间(秒)
|
|
168
|
+
* @returns 创建的Chart对象
|
|
169
|
+
*/
|
|
120
170
|
static fromRPEJSON(data: ChartDataRPE, duration: number) {
|
|
121
171
|
const chart = new Chart();
|
|
122
172
|
chart.judgeLineGroups = data.judgeLineGroup.map(group => new JudgeLineGroup(group));
|
|
@@ -127,11 +177,10 @@ export class Chart {
|
|
|
127
177
|
chart.charter = data.META.charter ?? "unknown";
|
|
128
178
|
chart.illustrator = data.META.illustration ?? "unknown";
|
|
129
179
|
chart.duration = duration;
|
|
130
|
-
chart.
|
|
131
|
-
chart.
|
|
132
|
-
chart.
|
|
180
|
+
chart.chartingSeconds = data.kpaChartTime ?? 0;
|
|
181
|
+
chart.rpeChartingSeconds = data.chartTime ?? 0;
|
|
182
|
+
chart.chartingSeconds = 0;
|
|
133
183
|
chart.initCalculator(data.BPMList)
|
|
134
|
-
console.log(chart, chart.getEffectiveBeats())
|
|
135
184
|
chart.nnnList = new NNNList(chart.getEffectiveBeats())
|
|
136
185
|
|
|
137
186
|
|
|
@@ -149,6 +198,7 @@ export class Chart {
|
|
|
149
198
|
const father = data.father === -1 ? null : judgeLineList[data.father];
|
|
150
199
|
if (father) {
|
|
151
200
|
father.children.add(line);
|
|
201
|
+
line.father = father;
|
|
152
202
|
} else {
|
|
153
203
|
chart.orphanLines.push(line);
|
|
154
204
|
}
|
|
@@ -157,6 +207,11 @@ export class Chart {
|
|
|
157
207
|
return chart
|
|
158
208
|
}
|
|
159
209
|
|
|
210
|
+
/**
|
|
211
|
+
* 从KPA格式的JSON数据创建谱面对象
|
|
212
|
+
* @param data KPA格式的谱面数据
|
|
213
|
+
* @returns 创建的Chart对象
|
|
214
|
+
*/
|
|
160
215
|
static fromKPAJSON(data: ChartDataKPA | ChartDataKPA2) {
|
|
161
216
|
const chart = new Chart();
|
|
162
217
|
|
|
@@ -168,9 +223,15 @@ export class Chart {
|
|
|
168
223
|
chart.charter = data.info.charter ?? "unknown";
|
|
169
224
|
chart.offset = data.offset;
|
|
170
225
|
chart.judgeLineGroups = data.judgeLineGroups.map(group => new JudgeLineGroup(group));
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
226
|
+
|
|
227
|
+
const interpreteAsSecs = data.version && data.version >= 203;
|
|
228
|
+
if (interpreteAsSecs) {
|
|
229
|
+
chart.chartingSeconds = data.chartTime ?? 0;
|
|
230
|
+
chart.rpeChartingSeconds = data.rpeChartTime ?? 0;
|
|
231
|
+
} else {
|
|
232
|
+
chart.chartingSeconds = data.chartTime ? data.chartTime * 60 : 0;
|
|
233
|
+
chart.rpeChartingSeconds = data.rpeChartTime ? data.rpeChartTime * 60 : 0;
|
|
234
|
+
}
|
|
174
235
|
|
|
175
236
|
chart.initCalculator(data.bpmList);
|
|
176
237
|
chart.nnnList = new NNNList(chart.getEffectiveBeats());
|
|
@@ -197,11 +258,14 @@ export class Chart {
|
|
|
197
258
|
seqData.events as EventDataKPA2<VT>[],
|
|
198
259
|
chart,
|
|
199
260
|
seqData.id,
|
|
200
|
-
seqData.
|
|
261
|
+
seqData.final as FinalEventStartNodeDataKPA2<VT>
|
|
201
262
|
);
|
|
202
263
|
sequence.id = seqData.id;
|
|
203
264
|
chart.sequenceMap.set(sequence.id, sequence);
|
|
204
265
|
}
|
|
266
|
+
|
|
267
|
+
chart.macroLib.readTimeMacros((data as ChartDataKPA2).timeMacros ?? []);
|
|
268
|
+
chart.macroLib.readValueMacros((data as ChartDataKPA2).valueMacros ?? []);
|
|
205
269
|
} else {
|
|
206
270
|
|
|
207
271
|
const sequences = (data as ChartDataKPA).eventNodeSequences
|
|
@@ -250,11 +314,21 @@ export class Chart {
|
|
|
250
314
|
}
|
|
251
315
|
return chart;
|
|
252
316
|
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* 初始化时间计算器
|
|
320
|
+
* @param bpmList BPM变化列表
|
|
321
|
+
*/
|
|
253
322
|
initCalculator(bpmList: BPMSegmentData[]) {
|
|
254
323
|
this.timeCalculator.bpmList = bpmList;
|
|
255
324
|
this.timeCalculator.duration = this.duration;
|
|
256
325
|
this.timeCalculator.initSequence()
|
|
257
326
|
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* 更新有效节拍数
|
|
330
|
+
* @param duration 新的持续时间
|
|
331
|
+
*/
|
|
258
332
|
updateEffectiveBeats(duration: number) {
|
|
259
333
|
const EB = this.timeCalculator.secondsToBeats(duration);
|
|
260
334
|
for (let i = 0; i < this.judgeLines.length; i++) {
|
|
@@ -262,6 +336,11 @@ export class Chart {
|
|
|
262
336
|
judgeLine.updateEffectiveBeats(EB);
|
|
263
337
|
}
|
|
264
338
|
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* 导出为KPA格式数据
|
|
342
|
+
* @returns KPA格式的谱面数据对象
|
|
343
|
+
*/
|
|
265
344
|
dumpKPA(): Required<ChartDataKPA2> {
|
|
266
345
|
const eventNodeSequenceCollector = new Set<EventNodeSequence>();
|
|
267
346
|
const orphanLines = [];
|
|
@@ -275,10 +354,14 @@ export class Chart {
|
|
|
275
354
|
}
|
|
276
355
|
return {
|
|
277
356
|
version: VERSION,
|
|
357
|
+
$schema: SCHEMA,
|
|
278
358
|
duration: this.duration,
|
|
279
359
|
bpmList: this.timeCalculator.dump(),
|
|
280
360
|
templateEasings: envEasings,
|
|
281
361
|
wrapperEasings: this.templateEasingLib.dumpWrapperEasings(),
|
|
362
|
+
macroEvaluators: this.macroLib.dumpMacroEvaluators(),
|
|
363
|
+
timeMacros: this.macroLib.dumpTimeMacros(),
|
|
364
|
+
valueMacros: this.macroLib.dumpValueMacros(),
|
|
282
365
|
eventNodeSequences: eventNodeSequenceData,
|
|
283
366
|
info: {
|
|
284
367
|
level: this.level,
|
|
@@ -299,18 +382,25 @@ export class Chart {
|
|
|
299
382
|
offset: this.offset,
|
|
300
383
|
orphanLines: orphanLines,
|
|
301
384
|
judgeLineGroups: this.judgeLineGroups.map(g => g.name),
|
|
302
|
-
chartTime: this.
|
|
303
|
-
rpeChartTime: this.
|
|
385
|
+
chartTime: this.chartingSeconds,
|
|
386
|
+
rpeChartTime: this.rpeChartingSeconds
|
|
304
387
|
};
|
|
305
388
|
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* 创建一个新的二级音符节点
|
|
392
|
+
* @param time 节点时间
|
|
393
|
+
* @returns 新创建的NNNode对象
|
|
394
|
+
*/
|
|
306
395
|
createNNNode(time: TimeT) {
|
|
307
396
|
return new NNNode(time)
|
|
308
397
|
}
|
|
398
|
+
|
|
309
399
|
/**
|
|
310
|
-
*
|
|
311
|
-
* @param type
|
|
312
|
-
* @param name
|
|
313
|
-
* @returns
|
|
400
|
+
* 创建一个新的事件节点序列
|
|
401
|
+
* @param type 事件类型
|
|
402
|
+
* @param name 序列名称(ID)
|
|
403
|
+
* @returns 新创建的事件节点序列
|
|
314
404
|
* @throws {KPAError<ERROR_IDS.SEQUENCE_NAME_OCCUPIED>}
|
|
315
405
|
*/
|
|
316
406
|
createEventNodeSequence<T extends EventType>(type: T, name: string) {
|
|
@@ -322,6 +412,15 @@ export class Chart {
|
|
|
322
412
|
this.sequenceMap.set(name, seq);
|
|
323
413
|
return seq;
|
|
324
414
|
}
|
|
415
|
+
|
|
416
|
+
registerEventNodeSequence<T extends EventType>(type: T, name: string, seq: EventNodeSequence<T>) {
|
|
417
|
+
if (this.sequenceMap.has(name)) {
|
|
418
|
+
throw err.SEQUENCE_NAME_OCCUPIED(name);
|
|
419
|
+
}
|
|
420
|
+
seq.id = name;
|
|
421
|
+
this.sequenceMap.set(name, seq);
|
|
422
|
+
}
|
|
423
|
+
|
|
325
424
|
/**
|
|
326
425
|
* 对谱面物量进行重新计数。
|
|
327
426
|
*
|
|
@@ -346,6 +445,7 @@ export class Chart {
|
|
|
346
445
|
}
|
|
347
446
|
this.maxCombo = combo;
|
|
348
447
|
}
|
|
448
|
+
|
|
349
449
|
/**
|
|
350
450
|
* 将UI绑定到某判定线
|
|
351
451
|
* @param ui UI名称,与RPEJSON中的代号相同
|
|
@@ -360,6 +460,7 @@ export class Chart {
|
|
|
360
460
|
this[key] = judgeLine;
|
|
361
461
|
judgeLine.hasAttachUI = true;
|
|
362
462
|
}
|
|
463
|
+
|
|
363
464
|
/**
|
|
364
465
|
* 移除谱面中某个UI的绑定,使UI进入未绑定状态
|
|
365
466
|
* @param ui UI名称,与RPEJSON中的相同
|
|
@@ -384,6 +485,12 @@ export class Chart {
|
|
|
384
485
|
judgeLine.hasAttachUI = false;
|
|
385
486
|
}
|
|
386
487
|
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* 查询指定判定线上绑定的UI组件
|
|
491
|
+
* @param judgeLine 目标判定线
|
|
492
|
+
* @returns 绑定到该判定线上的UI组件名称数组
|
|
493
|
+
*/
|
|
387
494
|
queryJudgeLineUI(judgeLine: JudgeLine): UIName[] {
|
|
388
495
|
const arr: UIName[] = [];
|
|
389
496
|
for (const ui of ["combo", "combonumber", "score", "pause", "bar", "name", "level"] satisfies UIName[]) {
|
|
@@ -393,11 +500,12 @@ export class Chart {
|
|
|
393
500
|
}
|
|
394
501
|
return arr;
|
|
395
502
|
}
|
|
503
|
+
|
|
396
504
|
/**
|
|
397
505
|
* 扫描所有用到的判定线贴图(纹理)并返回
|
|
398
506
|
*
|
|
399
507
|
* 给谱面播放器的接口,谱面播放器需要在初加载时提供贴图
|
|
400
|
-
* @returns
|
|
508
|
+
* @returns 所有使用的纹理名称集合
|
|
401
509
|
*/
|
|
402
510
|
scanAllTextures() {
|
|
403
511
|
const textures: Set<string> = new Set;
|
|
@@ -406,12 +514,13 @@ export class Chart {
|
|
|
406
514
|
}
|
|
407
515
|
return textures
|
|
408
516
|
}
|
|
517
|
+
|
|
409
518
|
/**
|
|
410
519
|
* 使用KPA2数据创建一个缓动对象。
|
|
411
520
|
*
|
|
412
521
|
* 只有对贝塞尔缓动和截段缓动才会创建新对象,其他几种缓动从缓动库等中的对象池获取
|
|
413
|
-
* @param data
|
|
414
|
-
* @returns
|
|
522
|
+
* @param data 缓动数据
|
|
523
|
+
* @returns 创建的缓动对象
|
|
415
524
|
*/
|
|
416
525
|
createEasingFromData(data: EasingDataKPA2) {
|
|
417
526
|
switch (data.type) {
|
|
@@ -420,37 +529,43 @@ export class Chart {
|
|
|
420
529
|
case EasingType.normal:
|
|
421
530
|
return rpeEasingArray[data.identifier];
|
|
422
531
|
case EasingType.segmented:
|
|
423
|
-
return new SegmentedEasing(this.createEasingFromData(data), data.left, data.right);
|
|
532
|
+
return new SegmentedEasing(this.createEasingFromData(data.inner), data.left, data.right);
|
|
424
533
|
case EasingType.template:
|
|
425
534
|
return this.templateEasingLib.get(data.identifier);
|
|
426
535
|
case EasingType.wrapper:
|
|
427
536
|
return this.templateEasingLib.getWrapper(data.identifier);
|
|
428
537
|
}
|
|
429
538
|
}
|
|
539
|
+
|
|
430
540
|
/**
|
|
431
541
|
* 使用KPA2数据创建一个求值器对象。
|
|
432
542
|
*
|
|
433
543
|
* 求值器只有缓动型和表达式型两类。
|
|
434
|
-
* @param data
|
|
435
|
-
* @param type
|
|
436
|
-
* @returns
|
|
544
|
+
* @param data 求值器数据
|
|
545
|
+
* @param type 事件值类型
|
|
546
|
+
* @returns 创建的求值器对象
|
|
437
547
|
*/
|
|
438
|
-
|
|
548
|
+
bindEvaluator<T extends EventValueESType>(node: EventStartNode<T>, data: EvaluatorDataKPA2<T>, type: EventValueTypeOfType<T>, pos: string) {
|
|
439
549
|
switch (data.type) {
|
|
440
550
|
case EvaluatorType.eased:
|
|
441
551
|
// 我管你这的那的 —— 小奶椰
|
|
442
|
-
|
|
552
|
+
node.evaluator = this.createEasedEvaluator(data, type) as unknown as Evaluator<T>;
|
|
553
|
+
break;
|
|
443
554
|
case EvaluatorType.expressionbased:
|
|
444
|
-
|
|
555
|
+
node.evaluator = this.createExpressionEvaluator(data) as ExpressionEvaluator<T>;
|
|
556
|
+
break;
|
|
557
|
+
case EvaluatorType.macro:
|
|
558
|
+
this.bindMacroEvaluator(node, data, pos);
|
|
445
559
|
}
|
|
446
560
|
}
|
|
561
|
+
|
|
447
562
|
/**
|
|
448
563
|
* 使用KPA2数据创建一个缓动求值器。
|
|
449
564
|
*
|
|
450
565
|
* 对于普通缓动,这些求值器是从对应类构造器的静态对象池属性中获取的。
|
|
451
|
-
* @param data
|
|
452
|
-
* @param type
|
|
453
|
-
* @returns
|
|
566
|
+
* @param data 缓动求值器数据
|
|
567
|
+
* @param type 事件值类型
|
|
568
|
+
* @returns 创建的缓动求值器对象
|
|
454
569
|
*/
|
|
455
570
|
createEasedEvaluator<T extends EventValueESType>(data: EasedEvaluatorDataOfType<T>, type: EventValueTypeOfType<T>): EasedEvaluatorOfType<T> {
|
|
456
571
|
switch (type) {
|
|
@@ -468,13 +583,14 @@ export class Chart {
|
|
|
468
583
|
: new TextEasedEvaluator(this.createEasingFromData(data.easing), (data as TextEasedEvaluatorKPA2).interpretedAs) as EasedEvaluatorOfType<T>
|
|
469
584
|
}
|
|
470
585
|
}
|
|
586
|
+
|
|
471
587
|
/**
|
|
472
588
|
* 用一个缓动和事件类型获取一个缓动求值器
|
|
473
|
-
* @param easing
|
|
474
|
-
* @param type
|
|
475
|
-
* @param interpreteAs
|
|
589
|
+
* @param easing 缓动对象
|
|
590
|
+
* @param type 事件值类型
|
|
591
|
+
* @param interpreteAs 文本解释方式
|
|
592
|
+
* @returns 对应类型的缓动求值器
|
|
476
593
|
*/
|
|
477
|
-
getEasedEvaluator<T extends string>(easing: Easing, type: EventValueType.text, interpreteAs: InterpreteAs): TextEasedEvaluator;
|
|
478
594
|
getEasedEvaluator<T extends EventValueESType>(easing: Easing, type: EventValueTypeOfType<T>, interpreteAs?: InterpreteAs): EasedEvaluatorOfType<T> {
|
|
479
595
|
const easingIsNormal = easing instanceof NormalEasing;
|
|
480
596
|
switch (type) {
|
|
@@ -492,22 +608,111 @@ export class Chart {
|
|
|
492
608
|
: new TextEasedEvaluator(easing, interpreteAs) as EasedEvaluatorOfType<T>
|
|
493
609
|
}
|
|
494
610
|
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* 根据JavaScript表达式创建表达式求值器
|
|
614
|
+
* @param data 表达式求值器数据
|
|
615
|
+
* @returns 创建的表达式求值器对象
|
|
616
|
+
*/
|
|
495
617
|
createExpressionEvaluator<T extends EventValueESType>(data: ExpressionEvaluatorDataKPA2) {
|
|
496
618
|
return new ExpressionEvaluator<T>(data.jsExpr);
|
|
497
619
|
}
|
|
620
|
+
|
|
621
|
+
bindMacroEvaluator(node: EventStartNode<EventValueESType>, data: MacroEvaluatorDataKPA2, pos: string) {
|
|
622
|
+
const key = data.name;
|
|
623
|
+
if (!key) {
|
|
624
|
+
throw err.MISSING_MACRO_EVALUATOR_KEY(pos);
|
|
625
|
+
}
|
|
626
|
+
const evaluator = this.macroLib.macroEvaluators.get(key);
|
|
627
|
+
if (!evaluator) {
|
|
628
|
+
throw err.MACRO_EVALUATOR_NOT_FOUND(key, pos);
|
|
629
|
+
}
|
|
630
|
+
node.evaluator = evaluator;
|
|
631
|
+
evaluator.consumers.set(node, new ExpressionEvaluator(data.compiled || "0"));
|
|
632
|
+
}
|
|
633
|
+
|
|
498
634
|
/**
|
|
499
635
|
* 使用KPA2JSON创建一对面对面节点
|
|
500
|
-
* @param data
|
|
501
|
-
* @param type
|
|
502
|
-
* @returns
|
|
636
|
+
* @param data 事件数据
|
|
637
|
+
* @param type 事件值类型
|
|
638
|
+
* @returns 包含起始节点和结束节点的元组
|
|
503
639
|
*/
|
|
504
|
-
createEventFromData<VT extends EventValueESType>(data: EventDataKPA2<VT>, type: EventValueTypeOfType<VT
|
|
640
|
+
createEventFromData<VT extends EventValueESType>(data: EventDataKPA2<VT>, type: EventValueTypeOfType<VT>, pos: string): [EventStartNode<VT>, EventEndNode<VT>] {
|
|
505
641
|
const start = new EventStartNode(data.startTime, data.start);
|
|
506
642
|
const end = new EventEndNode(data.endTime, data.end);
|
|
507
|
-
|
|
643
|
+
this.bindEvaluator(start, data.evaluator, type, pos);
|
|
644
|
+
if (data.macroStart) {
|
|
645
|
+
this.bindValueMacro(start, data.macroStart, pos)
|
|
646
|
+
}
|
|
647
|
+
if (data.macroEnd) {
|
|
648
|
+
this.bindValueMacro(end, data.macroEnd, pos)
|
|
649
|
+
}
|
|
650
|
+
if (data.macroStartTime) {
|
|
651
|
+
this.bindTimeMacro(start, data.macroStartTime, pos)
|
|
652
|
+
}
|
|
653
|
+
if (data.startLinkedMacro) {
|
|
654
|
+
for (const macroLink of data.startLinkedMacro) {
|
|
655
|
+
this.linkMacro(start, macroLink, pos)
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
if (data.endLinkedMacro) {
|
|
659
|
+
for (const macroLink of data.endLinkedMacro) {
|
|
660
|
+
this.linkMacro(end, macroLink, pos)
|
|
661
|
+
}
|
|
662
|
+
}
|
|
508
663
|
EventNode.connect(start, end);
|
|
509
664
|
return [start, end];
|
|
510
665
|
}
|
|
666
|
+
createFinalEventStartNodeFromData<VT extends EventValueESType>(data: FinalEventStartNodeDataKPA2<VT>, type: EventValueTypeOfType<VT>, pos: string): EventStartNode<VT> {
|
|
667
|
+
const node = new EventStartNode(data.startTime, data.start);
|
|
668
|
+
this.bindEvaluator(node, data.evaluator, type, pos);
|
|
669
|
+
if (typeof data.macro === "string") {
|
|
670
|
+
this.bindValueMacro(node, data.macro, pos);
|
|
671
|
+
}
|
|
672
|
+
if (typeof data.macroTime === "string") {
|
|
673
|
+
this.bindTimeMacro(node, data.macroTime, pos);
|
|
674
|
+
}
|
|
675
|
+
if (data.linkedMacro) {
|
|
676
|
+
for (const macroLink of data.linkedMacro) {
|
|
677
|
+
this.linkMacro(node, macroLink, pos)
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return node;
|
|
681
|
+
}
|
|
682
|
+
bindTimeMacro(node: EventStartNode<any>, macroData: MacroData, pos: string) {
|
|
683
|
+
const id = macroData[0];
|
|
684
|
+
const macro = this.macroLib.timeMacros.get(id);
|
|
685
|
+
if (typeof macro === "object" && macro instanceof EventMacroTime) {
|
|
686
|
+
node.macroTime = macro;
|
|
687
|
+
macro.bindNode(node, macroData, pos);
|
|
688
|
+
} else {
|
|
689
|
+
err.TIME_MACRO_NOT_FOUND(id, pos).warn();
|
|
690
|
+
}
|
|
691
|
+
return null;
|
|
692
|
+
}
|
|
693
|
+
bindValueMacro(node: EventNode<any>, macroData: MacroData, pos: string) {
|
|
694
|
+
const id = macroData[0];
|
|
695
|
+
const macro = this.macroLib.valueMacros.get(id);
|
|
696
|
+
if (typeof macro === "object" && macro instanceof EventMacroValue) {
|
|
697
|
+
node.macroValue = macro;
|
|
698
|
+
macro.bindNode(node, macroData, pos);
|
|
699
|
+
} else {
|
|
700
|
+
err.VALUE_MACRO_NOT_FOUND(id, pos).warn();
|
|
701
|
+
}
|
|
702
|
+
return null;
|
|
703
|
+
}
|
|
704
|
+
linkMacro(node: EventNode<EventValueESType>, linkData: MacroLink, pos: string) {
|
|
705
|
+
const prefix = linkData[0].split(":")[0] as 'time' | 'value';
|
|
706
|
+
const macroDict = prefix === 'time' ? this.macroLib.timeMacros : this.macroLib.valueMacros;
|
|
707
|
+
const realName = linkData[0].substring(prefix.length + 1);
|
|
708
|
+
const macro = macroDict.get(realName);
|
|
709
|
+
if (typeof macro === "object" && macro instanceof EventMacro) {
|
|
710
|
+
macro.linkProtoNode(node, linkData[1]);
|
|
711
|
+
} else {
|
|
712
|
+
err[`${prefix.toUpperCase() as 'TIME' | 'VALUE'}_MACRO_NOT_FOUND`](realName, pos).warn();
|
|
713
|
+
}
|
|
714
|
+
return null;
|
|
715
|
+
}
|
|
511
716
|
/* 暂时不用此方法,因为谱面播放器里面还是用反解法弄的
|
|
512
717
|
updateNNListsFromENS(speedENS: SpeedENS) {
|
|
513
718
|
|
|
@@ -515,14 +720,27 @@ export class Chart {
|
|
|
515
720
|
*/
|
|
516
721
|
}
|
|
517
722
|
|
|
723
|
+
/**
|
|
724
|
+
* 表示一组判定线的容器
|
|
725
|
+
*
|
|
726
|
+
* 用于组织和管理具有相同属性或用途的判定线集合
|
|
727
|
+
*/
|
|
518
728
|
export class JudgeLineGroup {
|
|
519
729
|
/**
|
|
520
730
|
* 该只读标记只是为了防止外部修改,内部可以修改
|
|
731
|
+
*
|
|
732
|
+
* 属于该组的判定线列表,按ID升序排列
|
|
521
733
|
*/
|
|
522
734
|
judgeLines: readonly JudgeLine[];
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* 创建一个新的判定线组
|
|
738
|
+
* @param name 组名称
|
|
739
|
+
*/
|
|
523
740
|
constructor(public name: string) {
|
|
524
741
|
this.judgeLines = []
|
|
525
742
|
}
|
|
743
|
+
|
|
526
744
|
/**
|
|
527
745
|
* 向判定线组添加一条判定线,并且保证其内部的判定线ID是升序排列的。
|
|
528
746
|
* @param judgeLine 要添加的判定线
|
|
@@ -548,9 +766,10 @@ export class JudgeLineGroup {
|
|
|
548
766
|
judgeLines.push(judgeLine);
|
|
549
767
|
|
|
550
768
|
}
|
|
769
|
+
|
|
551
770
|
/**
|
|
552
771
|
* 从判定线组移除一条判定线
|
|
553
|
-
* @param judgeLine
|
|
772
|
+
* @param judgeLine 要移除的判定线
|
|
554
773
|
*/
|
|
555
774
|
remove(judgeLine: JudgeLine) {
|
|
556
775
|
// 只读仅对外部作限制
|
|
@@ -560,8 +779,9 @@ export class JudgeLineGroup {
|
|
|
560
779
|
judgeLines.splice(index, 1);
|
|
561
780
|
}
|
|
562
781
|
}
|
|
782
|
+
|
|
563
783
|
/**
|
|
564
|
-
*
|
|
784
|
+
* 检查该判定线组是否为默认组
|
|
565
785
|
* @returns 该判定线组是否为默认判定线组,默认的判断标准是:名称为 "Default"(大小写不敏感)
|
|
566
786
|
*/
|
|
567
787
|
isDefault() {
|