kipphi 2.0.0 → 2.0.1

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/event.ts CHANGED
@@ -1,11 +1,16 @@
1
1
  import type { Chart } from "./chart";
2
- import { EventType, type TimeT, type EventDataKPA, type RGB, type EventDataRPELike, InterpreteAs, type ValueTypeOfEventType, type EventNodeSequenceDataKPA, type EventDataKPA2, type EventNodeSequenceDataKPA2, type EventValueESType, EventValueType } from "./chartTypes";
2
+ import { EventType, type TimeT, type EventDataKPA, type RGB, type EventDataRPELike, InterpreteAs, type ValueTypeOfEventType, type EventNodeSequenceDataKPA, type EventDataKPA2, type EventNodeSequenceDataKPA2, type EventValueESType, EventValueType, EventValueTypeOfType } from "./chartTypes";
3
3
  import { TemplateEasingLib, BezierEasing, Easing, rpeEasingArray, SegmentedEasing, linearEasing, fixedEasing, TemplateEasing, NormalEasing } from "./easing";
4
4
  import { ColorEasedEvaluator, EasedEvaluator, ExpressionEvaluator, NumericEasedEvaluator, TextEasedEvaluator, type Evaluator } from "./evaluator";
5
5
  import { JumpArray } from "./jumparray";
6
- import { TimeCalculator, TC } from "./time";
6
+
7
+ import TC from "./time";
8
+ import { type TimeCalculator } from "./bpm";
7
9
  import { NodeType } from "./util";
8
10
 
11
+ import { err, ERROR_IDS, KPAError } from "./env";
12
+ import { JudgeLine } from "./judgeline";
13
+
9
14
  /// #declaration:global
10
15
  export class EventNodeLike<T extends NodeType, VT = number> {
11
16
  type: T;
@@ -44,8 +49,8 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
44
49
  evaluator: Evaluator<VT>;
45
50
  constructor(time: TimeT, value: VT) {
46
51
  super(NodeType.MIDDLE);
47
- this.time = TimeCalculator.validateIp([...time]);
48
- // @ts-ignore 不清楚什么时候会是undefined,但是留着准没错
52
+ this.time = TC.validateIp([...time]);
53
+ // @ts-expect-error 不清楚什么时候会是undefined,但是留着准没错((
49
54
  this.value = value ?? 0;
50
55
  if (typeof value === "number") {
51
56
  this.evaluator = NumericEasedEvaluator.default as unknown as Evaluator<VT>;
@@ -57,7 +62,7 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
57
62
  }
58
63
  clone(offset: TimeT): EventStartNode<VT> | EventEndNode<VT> {
59
64
  const ret = new (this.constructor as (typeof EventStartNode | typeof EventEndNode))
60
- (offset ? TimeCalculator.add(this.time, offset) : this.time, this.value);
65
+ (offset ? TC.add(this.time, offset) : this.time, this.value);
61
66
  ret.evaluator = this.evaluator;
62
67
  return ret;
63
68
  }
@@ -69,15 +74,15 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
69
74
  * @returns
70
75
  * @deprecated
71
76
  */
72
- static getEasing(data: EventDataKPA<any>, templates: TemplateEasingLib): Easing {
77
+ static getEasing(data: EventDataKPA<EventValueESType>, templates: TemplateEasingLib, notSegmented = false): Easing {
73
78
  const left = data.easingLeft;
74
79
  const right = data.easingRight;
75
- if ((left && right) && (left !== 0.0 || right !== 1.0)) {
76
- return new SegmentedEasing(EventNode.getEasing(data, templates), left, right)
80
+ if (!notSegmented && (left && right) && (left !== 0.0 || right !== 1.0)) {
81
+ return new SegmentedEasing(EventNode.getEasing(data, templates, true), left, right)
77
82
  }
78
83
  if (data.bezier) {
79
- let bp = data.bezierPoints
80
- let easing = new BezierEasing([bp[0], bp[1]], [bp[2], bp[3]]);
84
+ const bp = data.bezierPoints
85
+ const easing = new BezierEasing([bp[0], bp[1]], [bp[2], bp[3]]);
81
86
  return easing
82
87
  } else if (typeof data.easingType === "string") {
83
88
  return templates.get(data.easingType);
@@ -96,24 +101,25 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
96
101
  * @returns
97
102
  * @deprecated
98
103
  */
99
- static getEvaluator(data: EventDataKPA<any>, templates: TemplateEasingLib): Evaluator<any> {
104
+ static getEvaluator<VT extends EventValueESType>(data: EventDataKPA<VT>, templates: TemplateEasingLib, interpreteAs?: InterpreteAs): Evaluator<VT> {
100
105
  const left = data.easingLeft;
101
106
  const right = data.easingRight;
102
- const wrap = (easing: Easing) => {
107
+ const wrap = (easing: Easing): EasedEvaluator<VT> => {
103
108
  if (typeof data.start === "number") {
104
- return new NumericEasedEvaluator(easing);
109
+ return new NumericEasedEvaluator(easing) as EasedEvaluator<VT>;
105
110
  } else if (typeof data.start === "string") {
106
- return new TextEasedEvaluator(easing, data.interpreteAs);
111
+ // @ts-expect-error
112
+ return new TextEasedEvaluator(easing, interpreteAs);
107
113
  } else {
108
- return new ColorEasedEvaluator(easing);
114
+ return new ColorEasedEvaluator(easing) as EasedEvaluator<VT>;
109
115
  }
110
- }
116
+ };
111
117
  if ((left && right) && (left !== 0.0 || right !== 1.0)) {
112
118
  return wrap(new SegmentedEasing(EventNode.getEasing(data, templates), left, right))
113
119
  }
114
120
  if (data.bezier) {
115
- let bp = data.bezierPoints
116
- let easing = new BezierEasing([bp[0], bp[1]], [bp[2], bp[3]]);
121
+ const bp = data.bezierPoints
122
+ const easing = new BezierEasing([bp[0], bp[1]], [bp[2], bp[3]]);
117
123
  return wrap(easing)
118
124
  } else if (data.isParametric) {
119
125
  if (typeof data.easingType !== "string") {
@@ -137,8 +143,8 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
137
143
  * @returns
138
144
  */
139
145
  static fromEvent<VT extends RGB | number>(data: EventDataRPELike<VT>, chart: Chart): [EventStartNode<VT>, EventEndNode<VT>] {
140
- let start = new EventStartNode(data.startTime, data.start)
141
- let end = new EventEndNode(data.endTime, data.end);
146
+ const start = new EventStartNode(data.startTime, data.start)
147
+ const end = new EventEndNode(data.endTime, data.end);
142
148
  start.evaluator = EventNode.getEvaluator(data, chart.templateEasingLib);
143
149
  EventNode.connect(start, end);
144
150
  return [start, end]
@@ -161,10 +167,9 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
161
167
  interpreteAs = InterpreteAs.int;
162
168
  }
163
169
  }
164
- let start = new EventStartNode<string>(data.startTime, startValue);
165
- let end = new EventEndNode<string>(data.endTime, endValue);
166
- start.interpretedAs = interpreteAs;
167
- start.evaluator = EventNode.getEvaluator(data, templates);
170
+ const start = new EventStartNode<string>(data.startTime, startValue);
171
+ const end = new EventEndNode<string>(data.endTime, endValue);
172
+ start.evaluator = EventNode.getEvaluator(data, templates, (data as unknown as EventDataKPA).interpreteAs ?? interpreteAs);
168
173
  EventNode.connect(start, end)
169
174
  return [start, end]
170
175
  }
@@ -197,7 +202,7 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
197
202
  static insert<VT>(node: EventStartNode<VT>, tarPrev: EventStartNode<VT>): [EventNodeLike<NodeType.HEAD, VT> | EventStartNode<VT>, EventStartNode<VT> | EventNodeLike<NodeType.TAIL, VT>] {
198
203
  const tarNext = tarPrev.next;
199
204
  if (node.previous.type === NodeType.HEAD) {
200
- throw new Error("Cannot insert a head node before any node");
205
+ throw err.CANNOT_INSERT_BEFORE_HEAD();
201
206
  }
202
207
  this.connect(tarPrev, node.previous);
203
208
  node.parentSeq = node.previous.parentSeq;
@@ -259,17 +264,17 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
259
264
  } else if (node instanceof EventEndNode) {
260
265
  return [<EventStartNode<VT>>node.previous, node]
261
266
  } else {
262
- throw new Error("Invalid node type")
267
+ throw new Error("unreachable");
263
268
  }
264
269
  }
265
270
  static setToNewOrderedArray<VT>(dest: TimeT, set: Set<EventStartNode<VT>>): [EventStartNode<VT>[], EventStartNode<VT>[]] {
266
271
  const nodes = [...set]
267
- nodes.sort((a, b) => TimeCalculator.gt(a.time, b.time) ? 1 : -1);
268
- const offset = TimeCalculator.sub(dest, nodes[0].time)
272
+ nodes.sort((a, b) => TC.gt(a.time, b.time) ? 1 : -1);
273
+ const offset = TC.sub(dest, nodes[0].time)
269
274
  return [nodes, nodes.map(node => node.clonePair(offset))]
270
275
  }
271
276
  static belongToSequence(nodes: Set<EventStartNode>, sequence: EventNodeSequence): boolean {
272
- for (let each of nodes) {
277
+ for (const each of nodes) {
273
278
  if (each.parentSeq !== sequence) {
274
279
  return false;
275
280
  }
@@ -297,76 +302,15 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
297
302
  // #endregion
298
303
  }
299
304
 
300
-
301
- const getValueFns = [
302
- (current: number, timeDelta: number, value: number, nextVal: number, easing: Easing) => {
303
-
304
- // @ts-ignore TSC脑壳有问题
305
- const valueDelta = nextVal - value
306
- // 其他类型,包括普通缓动和非钩定模板缓动
307
- return value + easing.getValue(current / timeDelta) * valueDelta
308
- },
309
- (current: number, timeDelta: number, value: string, nextVal: string, easing: Easing, interpretedAs: InterpreteAs): string => {
310
-
311
- if (interpretedAs === InterpreteAs.float) {
312
- const start = parseFloat(value);
313
- const delta = parseFloat(nextVal as string) - start;
314
- return start + easing.getValue(current / timeDelta) * delta + "";
315
- } else if (interpretedAs === InterpreteAs.int) {
316
- const start = parseInt(value);
317
- const delta = parseInt(nextVal as string) - start;
318
- return start + Math.round(easing.getValue(current / timeDelta) * delta) + "";
319
- } else if (value.startsWith(nextVal as string)) {
320
- const startLen = (nextVal as string).length;
321
- const deltaLen = value.length - startLen;
322
- const len = startLen + Math.floor(deltaLen * easing.getValue(current / timeDelta));
323
- return value.substring(0, len);
324
- } else if ((nextVal as string).startsWith(value)) {
325
- const startLen = value.length;
326
- const deltaLen = (nextVal as string).length - startLen;
327
- const len = startLen + Math.floor(deltaLen * easing.getValue(current / timeDelta));
328
- return (nextVal as string).substring(0, len);
329
- } else {
330
- return value;
331
- }
332
-
333
- },
334
- (current: number, timeDelta: number, value: RGB, nextValue: RGB, easing: Easing) => {
335
- return (value as RGB).map((v, i) => {
336
- const nextVal = nextValue[i];
337
- const value = v;
338
- if (nextVal === value) {
339
- return value;
340
- } else {
341
- const delta = nextVal - value;
342
- return value + easing.getValue(current / timeDelta) * delta;
343
- }
344
- })
345
- }
346
- ] as const;
347
-
348
-
349
-
350
305
  export class EventStartNode<VT = number> extends EventNode<VT> {
351
306
  override next: EventEndNode<VT> | EventNodeLike<NodeType.TAIL, VT>;
352
307
  override previous: EventEndNode<VT> | EventNodeLike<NodeType.HEAD, VT>;
353
308
  /**
354
- * 对于速度事件,从计算时的时刻到此节点的总积分
309
+ * 对于速度事件,从0时刻到此节点的总积分
355
310
  */
356
- cachedIntegral?: number;
311
+ floorPosition: number = 0;
357
312
  constructor(time: TimeT, value: VT) {
358
313
  super(time, value);
359
- // 最史的一集,what can i say
360
- if (typeof value === "number") {
361
- // @ts-ignore
362
- this.getValueFn = getValueFns[0];
363
- } else if (typeof value === "string") {
364
- // @ts-ignore
365
- this.getValueFn = getValueFns[1];
366
- } else {
367
- // @ts-ignore
368
- this.getValueFn = getValueFns[2];
369
- }
370
314
  }
371
315
  override parentSeq: EventNodeSequence<VT>;
372
316
  /**
@@ -384,35 +328,6 @@ export class EventStartNode<VT = number> extends EventNode<VT> {
384
328
  evaluator: this.evaluator.dump(),
385
329
  }
386
330
  }
387
- /**
388
- * 产生一个一拍长的短钩定事件
389
- * 仅用于编译至RPE时解决最后一个StartNode的问题
390
- * 限最后一个StartNode使用
391
- * @returns
392
- * /
393
- dumpAsLast(): EventDataRPELike<VT> {
394
- const isSegmented = this.easingIsSegmented
395
- const easing = isSegmented ? (this.easing as SegmentedEasing).easing : this.easing;
396
- return {
397
- bezier: easing instanceof BezierEasing ? 1 : 0,
398
- bezierPoints: easing instanceof BezierEasing ?
399
- [easing.cp1[0], easing.cp1[1], easing.cp2[0], easing.cp2[1]] : // 修正了这里 cp2.y 的引用
400
- [0, 0, 0, 0],
401
- easingLeft: isSegmented ? (this.easing as SegmentedEasing).left : 0.0,
402
- easingRight: isSegmented ? (this.easing as SegmentedEasing).right : 1.0,
403
- easingType: easing instanceof TemplateEasing ?
404
- (easing.name) :
405
- easing instanceof NormalEasing ?
406
- easing.rpeId :
407
- null,
408
- end: this.value,
409
- endTime: TimeCalculator.add(this.time, [1, 0, 1]),
410
- linkgroup: 0, // 假设默认值为 0
411
- start: this.value,
412
- startTime: this.time,
413
- }
414
- }//*/
415
- interpretedAs: InterpreteAs = InterpreteAs.str;
416
331
  getValueAt(beats: number): VT {
417
332
  // 除了尾部的开始节点,其他都有下个节点
418
333
  // 钩定型缓动也有
@@ -421,34 +336,29 @@ export class EventStartNode<VT = number> extends EventNode<VT> {
421
336
  }
422
337
  return this.evaluator.eval(this, beats);
423
338
  }
424
- private getValueFn: (current: number, timeDelta: number, value: VT, nextVal: VT, easing: Easing, interpreteAs?: InterpreteAs) => VT
425
339
  getSpeedValueAt(this: EventStartNode<number>, beats: number) {
426
340
  if (this.next.type === NodeType.TAIL) {
427
- return this.value
428
- }
429
- let timeDelta = TimeCalculator.getDelta(this.next.time, this.time)
430
- let valueDelta = this.next.value - this.value
431
- let current = beats - TimeCalculator.toBeats(this.time)
432
- if (current > timeDelta || current < 0) {
433
- console.warn("超过事件时间范围!", this, beats)
434
- // debugger
341
+ return this.value;
435
342
  }
343
+ const timeDelta = TC.getDelta(this.next.time, this.time);
344
+ const valueDelta = this.next.value - this.value;
345
+ const current = beats - TC.toBeats(this.time);
436
346
  return this.value + linearEasing.getValue(current / timeDelta) * valueDelta;
437
347
  }
438
348
  /**
439
349
  * 积分获取位移
440
350
  */
441
- getIntegral(this: EventStartNode<number>, beats: number, timeCalculator: TimeCalculator) {
442
- return timeCalculator.segmentToSeconds(TimeCalculator.toBeats(this.time), beats) * (this.value + this.getSpeedValueAt(beats)) / 2 * 120 // 每单位120px
351
+ getLocalFloorPos(this: EventStartNode<number>, beats: number, timeCalculator: TimeCalculator) {
352
+ return timeCalculator.segmentToSeconds(TC.toBeats(this.time), beats) * (this.value + this.getSpeedValueAt(beats)) / 2 * 120 // 每单位120px
443
353
  }
444
- getFullIntegral(this: EventStartNode<number>, timeCalculator: TimeCalculator) {
354
+ getFullLocalFloorPos(this: EventStartNode<number>, timeCalculator: TimeCalculator) {
445
355
  if (this.next.type === NodeType.TAIL) {
446
356
  console.log(this)
447
- throw new Error("getFullIntegral不可用于尾部节点")
357
+ throw err.CANNOT_GET_FULL_INTEGRAL_OF_FINAL_START_NODE();
448
358
  }
449
- let end = this.next;
450
- let endBeats = TimeCalculator.toBeats(end.time)
451
- let startBeats = TimeCalculator.toBeats(this.time)
359
+ const end = this.next;
360
+ const endBeats = TC.toBeats(end.time)
361
+ const startBeats = TC.toBeats(this.time)
452
362
  // 原来这里写反了,气死偶咧!
453
363
  return timeCalculator.segmentToSeconds(startBeats, endBeats) * (this.value + end.value) / 2 * 120
454
364
  }
@@ -458,6 +368,9 @@ export class EventStartNode<VT = number> extends EventNode<VT> {
458
368
  isLastStart() {
459
369
  return this.next && this.next.type === NodeType.TAIL
460
370
  }
371
+ isSpeed() {
372
+ return this.parentSeq.type === EventType.speed;
373
+ }
461
374
  override clone(offset?: TimeT): EventStartNode<VT> {
462
375
  return super.clone(offset) as EventStartNode<VT>;
463
376
  };
@@ -467,32 +380,15 @@ export class EventStartNode<VT = number> extends EventNode<VT> {
467
380
  EventNode.connect(endNode, startNode);
468
381
  return startNode;
469
382
  };
470
-
471
-
472
- drawCurve(context: CanvasRenderingContext2D, startX: number, startY: number, endX: number , endY: number, matrix: Matrix): void {
473
- if (!(this.easing instanceof ParametricEquationEasing)) {
474
- return this.easing.drawCurve(context, startX, startY, endX, endY);
475
- }
476
- const getValue = (ratio: number) => {
477
- return matrix.ymul(0, this.easing.getValue(ratio))
478
- }
479
- const timeDelta = endX - startX;
480
- let last = startY;
481
- context.beginPath()
482
- context.moveTo(startX, last)
483
- for (let t = 4; t <= timeDelta; t += 4) {
484
- const ratio = t / timeDelta
485
- const curPosY = getValue(ratio);
486
- context.lineTo(startX + t, curPosY);
487
- last = curPosY;
488
- }
489
- context.stroke();
490
- }
491
383
  }
492
384
 
385
+ export type NonLastStartNode<VT extends EventValueESType> = EventStartNode<VT> & {next: EventEndNode<VT>}
386
+
493
387
  export class EventEndNode<VT = number> extends EventNode<VT> {
494
388
  override next!: EventStartNode<VT>;
495
389
  override previous!: EventStartNode<VT>;
390
+
391
+ // @ts-expect-error 这里没有类型安全问题
496
392
  override get parentSeq(): EventNodeSequence<VT> {return this.previous?.parentSeq || null}
497
393
  override set parentSeq(_parent: EventNodeSequence<VT>) {}
498
394
  constructor(time: TimeT, value: VT) {
@@ -507,6 +403,12 @@ export class EventEndNode<VT = number> extends EventNode<VT> {
507
403
  }
508
404
 
509
405
 
406
+ export enum Monotonicity {
407
+ increasing,
408
+ decreasing,
409
+ swinging
410
+ }
411
+
510
412
  /**
511
413
  * 为一个链表结构。会有一个数组进行快跳。
512
414
  * is the list of event nodes, but not purely start nodes.
@@ -534,31 +436,51 @@ export class EventEndNode<VT = number> extends EventNode<VT> {
534
436
  * Remember to update the jump array when inserting or deleting nodes.
535
437
  */
536
438
  export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
439
+ /**
440
+ * @deprecated 谱面属性未实装,以后有必要的时候会添加为其赋值的逻辑
441
+ */
537
442
  chart: Chart;
538
- /** id follows the format `#${lineid}.${layerid}.${typename}` by default */
443
+ /** 标识名默认遵循`#${lineid}.${layerid}.${typename}`格式
444
+ * id follows the format `#${lineid}.${layerid}.${typename}` by default
445
+ */
539
446
  id: string;
540
- /** has no time or value */
541
447
  head: EventNodeLike<NodeType.HEAD, VT>;
542
- /** has no time or value */
543
448
  tail: EventNodeLike<NodeType.TAIL, VT>;
544
449
  jump?: JumpArray<AnyEN<VT>>;
545
450
  listLength: number;
546
451
  /** 一定是二的幂,避免浮点误差 */
547
452
  jumpAverageBeats: number;
548
- // nodes: EventNode[];
549
- // startNodes: EventStartNode[];
550
- // endNodes: EventEndNode[];
551
- // eventTime: Float64Array;
453
+
454
+ /**
455
+ * 用于速度事件优化
456
+ */
457
+ monotonicity: Monotonicity = Monotonicity.swinging;
458
+
459
+ /**
460
+ * 使用了该序列的判定线,当前仅用于速度事件对fp的更新
461
+ */
462
+ consumerLines = new Set<JudgeLine>;
463
+
552
464
  constructor(public type: EventType, public effectiveBeats: number) {
553
465
  this.head = new EventNodeLike(NodeType.HEAD);
554
466
  this.tail = new EventNodeLike(NodeType.TAIL);
555
467
  this.head.parentSeq = this.tail.parentSeq = this;
556
468
  this.listLength = 1;
557
- // this.head = this.tail = new EventStartNode([0, 0, 0], 0)
558
- // this.nodes = [];
559
- // this.startNodes = [];
560
- // this.endNodes = [];
561
469
  }
470
+ /**
471
+ * 获取指定事件类型的事件默认值。
472
+ * Get the default value of the specified event type.
473
+ *
474
+ * 对于文字事件,默认值为空字符串。
475
+ *
476
+ * 对于缩放事件,默认值为1.0。
477
+ *
478
+ * 对于颜色事件,默认值为黑色([0, 0, 0])。
479
+ *
480
+ * 对于速度事件,默认值为10(1200像素/秒)。
481
+ *
482
+ * 对于一般数值事件,默认值为0。
483
+ */
562
484
  static getDefaultValueFromEventType(type: EventType) {
563
485
 
564
486
  return type === EventType.speed ? 10 :
@@ -567,15 +489,15 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
567
489
  type === EventType.color ? [0, 0, 0] :
568
490
  0
569
491
  }
570
- static fromRPEJSON<T extends EventType, VT = number>(type: T, data: EventDataRPELike<VT>[], chart: Chart, endValue?: number) {
492
+ static fromRPEJSON<T extends EventType, VT = number>(type: T, data: EventDataRPELike<VT>[], chart: Chart, pos: string, endValue?: number) {
571
493
  const {templateEasingLib: templates} = chart
572
494
  const length = data.length;
573
495
  // const isSpeed = type === EventType.Speed;
574
496
  // console.log(isSpeed)
575
- const seq = new EventNodeSequence<VT>(type, type === EventType.easing ? TimeCalculator.toBeats(data[length - 1].endTime) : chart.effectiveBeats);
497
+ const seq = new EventNodeSequence<VT>(type, type === EventType.easing ? TC.toBeats(data[length - 1].endTime) : chart.effectiveBeats);
576
498
  let listLength = length;
577
499
  let lastEnd: EventEndNode<VT> | EventNodeLike<NodeType.HEAD, VT> = seq.head;
578
- // 如果第一个事件不从0时间开始,那么添加一对面对面节点来垫背
500
+ // 如果第一个事件不从0时间开始,那么添加一对面对面节点来垫底
579
501
  if (data[0] && TC.ne(data[0].startTime, [0, 0, 1])) {
580
502
  const value = data[0].start
581
503
  const start = new EventStartNode<VT>([0, 0, 1], value as VT);
@@ -585,59 +507,80 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
585
507
  lastEnd = end;
586
508
  }
587
509
 
588
- let lastIntegral: number = 0;
510
+ let lastEndTime: TimeT = [0, 0, 1];
511
+
512
+ // 读取事件列表
589
513
  for (let index = 0; index < length; index++) {
590
514
  const event = data[index];
591
- // @ts-ignore
592
- let [start, end] = (type === EventType.text ? EventNode.fromTextEvent(event, templates) : EventNode.fromEvent(event, templates)) as unknown as [EventStartNode<VT>, EventEndNode<VT>];
515
+ if (TC.lt(event.startTime, lastEndTime)) { // event.startTime < lastEndTime
516
+ err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}] and the previous`).warn()
517
+ }
518
+ if (!TC.lt(event.startTime, event.endTime)) {
519
+ err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}]`).warn()
520
+ }
521
+ lastEndTime = event.endTime;
522
+ const [start, end] = (type === EventType.text
523
+ ? EventNode.fromTextEvent(event as EventDataRPELike<string>, templates)
524
+ : EventNode.fromEvent(event as EventDataRPELike<number | RGB>, chart)) as unknown as [EventStartNode<VT>, EventEndNode<VT>];
525
+ // 刚开始时,上个节点是头
593
526
  if (lastEnd.type === NodeType.HEAD) {
594
527
  EventNode.connect(lastEnd, start)
595
528
  // 如果上一个是钩定事件,那么一块捋平
596
529
  } else if (lastEnd.value === lastEnd.previous.value && lastEnd.previous.evaluator instanceof EasedEvaluator) {
597
530
  lastEnd.time = start.time
598
531
  EventNode.connect(lastEnd, start)
599
- } else if (TimeCalculator.toBeats(lastEnd.time) !== TimeCalculator.toBeats(start.time)) {
600
- let val = lastEnd.value;
601
- let midStart = new EventStartNode(lastEnd.time, val);
602
- let midEnd = new EventEndNode(start.time, val);
532
+
533
+ // 如果上一个事件的结束时间和下一个事件的开始时间不一样,那么中间插入一对面对面节点
534
+ } else if (TC.toBeats(lastEnd.time) !== TC.toBeats(start.time)) {
535
+ const val = lastEnd.value;
536
+ const midStart = new EventStartNode(lastEnd.time, val);
537
+ const midEnd = new EventEndNode(start.time, val);
603
538
  midStart.evaluator = lastEnd.previous.evaluator;
604
539
  EventNode.connect(lastEnd, midStart);
605
540
  EventNode.connect(midStart, midEnd);
606
541
  EventNode.connect(midEnd, start)
607
- // seq.startNodes.push(midStart);
608
- // seq.endNodes.push(midEnd);
609
542
  listLength++;
543
+ // 最后应该就是上一个事件和这个事件首尾相接的情况
610
544
  } else {
611
545
 
612
546
  EventNode.connect(lastEnd, start)
613
547
  }
614
548
 
615
- // seq.startNodes.push(start);
616
- // seq.endNodes.push(end);
617
549
  lastEnd = end;
618
- // seq.nodes.push(start, end);
619
550
  }
620
551
  const last = lastEnd;
621
552
  const tail = new EventStartNode(
622
553
  last.type === NodeType.HEAD ? [0, 0, 1] : last.time,
623
- last.type === NodeType.HEAD ? endValue : last.value
554
+ endValue ?? last.value
624
555
  );
625
556
  EventNode.connect(last, tail);
626
557
  // last can be a header, in which case easing is undefined.
627
558
  // then we use the easing that initialized in the EventStartNode constructor.
628
559
  tail.evaluator = last.previous?.evaluator ?? tail.evaluator;
629
- tail.cachedIntegral = lastIntegral
630
560
  EventNode.connect(tail, seq.tail)
631
561
  seq.listLength = listLength;
632
562
  seq.initJump();
563
+ if (type === EventType.speed) {
564
+ (seq as SpeedENS).updateFloorPositionAfter((seq as SpeedENS).head.next, chart.timeCalculator)
565
+ }
633
566
  return seq;
634
567
  }
635
- static fromKPA2JSON<T extends EventType, VT extends EventValueESType = number>(type: T, data: EventDataKPA2<VT>[], chart: Chart, endValue?: number) {
636
- const {templateEasingLib: templates} = chart
568
+ static fromKPA2JSON<T extends EventType, VT extends EventValueESType = number>(
569
+ type: T,
570
+ data: EventDataKPA2<VT>[],
571
+ chart: Chart,
572
+ pos: string,
573
+ endValue?: VT
574
+ )
575
+ {
637
576
  const length = data.length;
638
577
  // const isSpeed = type === EventType.Speed;
639
578
  // console.log(isSpeed)
640
- const seq = new EventNodeSequence<VT>(type, type === EventType.easing ? TimeCalculator.toBeats(data[length - 1].endTime) : chart.effectiveBeats);
579
+ const seq = new EventNodeSequence<VT>(
580
+ type,
581
+ type === EventType.easing
582
+ ? TC.toBeats(data[length - 1].endTime)
583
+ : chart.effectiveBeats);
641
584
  let listLength = length;
642
585
  let lastEnd: EventEndNode<VT> | EventNodeLike<NodeType.HEAD, VT> = seq.head;
643
586
  // 如果第一个事件不从0时间开始,那么添加一对面对面节点来垫背
@@ -650,24 +593,38 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
650
593
  lastEnd = end;
651
594
  }
652
595
 
653
- const valueType = type === EventType.color ? EventValueType.color
596
+ const valueType = (type === EventType.color ? EventValueType.color
654
597
  : type === EventType.text ? EventValueType.text
655
- : EventValueType.numeric;
598
+ : EventValueType.numeric) as EventValueTypeOfType<VT>;
656
599
 
657
- let lastIntegral: number = 0;
600
+
601
+
602
+ let lastEndTime: TimeT = [0, 0, 1];
658
603
  for (let index = 0; index < length; index++) {
659
604
  const event = data[index];
660
- let [start, end] = chart.createEventFromData<VT>(event, valueType);
605
+ const [start, end] = chart.createEventFromData<VT>(event, valueType);
606
+ // 从前面复制了,复用性减一
607
+ // KPA2没有更改RPE的按事件存储的机制。
608
+ if (TC.lt(event.startTime, lastEndTime)) { // event.startTime < lastEndTime
609
+ err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}] and the previous`).warn()
610
+ }
611
+ if (!TC.lt(event.startTime, event.endTime)) {
612
+ err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}]`).warn()
613
+ }
614
+ lastEndTime = event.endTime;
661
615
  if (lastEnd.type === NodeType.HEAD) {
662
616
  EventNode.connect(lastEnd, start)
663
617
  // 如果上一个是钩定事件,那么一块捋平
664
- } else if (lastEnd.value === lastEnd.previous.value && lastEnd.previous.evaluator instanceof EasedEvaluator) {
618
+ } else if (
619
+ lastEnd.value === lastEnd.previous.value
620
+ && lastEnd.previous.evaluator instanceof EasedEvaluator
621
+ ) {
665
622
  lastEnd.time = start.time
666
623
  EventNode.connect(lastEnd, start)
667
- } else if (TimeCalculator.toBeats(lastEnd.time) !== TimeCalculator.toBeats(start.time)) {
668
- let val = lastEnd.value;
669
- let midStart = new EventStartNode(lastEnd.time, val);
670
- let midEnd = new EventEndNode(start.time, val);
624
+ } else if (TC.eq(lastEnd.time, start.time)) {
625
+ const val = lastEnd.value;
626
+ const midStart = new EventStartNode(lastEnd.time, val);
627
+ const midEnd = new EventEndNode(start.time, val);
671
628
  midStart.evaluator = lastEnd.previous.evaluator;
672
629
  EventNode.connect(lastEnd, midStart);
673
630
  EventNode.connect(midStart, midEnd);
@@ -689,10 +646,12 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
689
646
  // last can be a header, in which case easing is undefined.
690
647
  // then we use the easing that initialized in the EventStartNode constructor.
691
648
  tail.evaluator = last.previous?.evaluator ?? tail.evaluator;
692
- tail.cachedIntegral = lastIntegral
693
649
  EventNode.connect(tail, seq.tail)
694
650
  seq.listLength = listLength;
695
651
  seq.initJump();
652
+ if (type === EventType.speed) {
653
+ (seq as SpeedENS).updateFloorPositionAfter((seq as SpeedENS).head.next, chart.timeCalculator)
654
+ }
696
655
  return seq;
697
656
  }
698
657
  /**
@@ -702,11 +661,16 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
702
661
  * @param effectiveBeats
703
662
  * @returns
704
663
  */
705
- static newSeq<T extends EventType>(type: T, effectiveBeats: number): EventNodeSequence<ValueTypeOfEventType<T>> {
664
+ static newSeq<T extends EventType>(
665
+ type: T,
666
+ effectiveBeats: number
667
+ ): EventNodeSequence<ValueTypeOfEventType<T>>
668
+ {
706
669
  type V = ValueTypeOfEventType<T>
707
670
  const sequence = new EventNodeSequence<V>(type, effectiveBeats);
708
671
  const node = new EventStartNode<V>(
709
- [0, 0, 1], EventNodeSequence.getDefaultValueFromEventType(type) as V
672
+ [0, 0, 1],
673
+ EventNodeSequence.getDefaultValueFromEventType(type) as V
710
674
  );
711
675
  EventNode.connect(sequence.head, node)
712
676
  EventNode.connect(node, sequence.tail)
@@ -736,7 +700,7 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
736
700
  return [0, node.next]
737
701
  }
738
702
  const endNode = (node as EventStartNode<VT>).next as EventEndNode<VT>;
739
- const time = TimeCalculator.toBeats(endNode.time);
703
+ const time = TC.toBeats(endNode.time);
740
704
  const nextNode = endNode.next;
741
705
  if (nextNode.next.type === NodeType.TAIL) {
742
706
  return [time, nextNode.next] // Tailer代替最后一个StartNode去占位
@@ -745,7 +709,7 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
745
709
  }
746
710
  },
747
711
  (node: EventStartNode<VT>, beats: number) => {
748
- return TimeCalculator.toBeats((node.next as EventEndNode<VT>).time) > beats ? false : EventNode.nextStartInJumpArray(node)
712
+ return TC.toBeats((node.next as EventEndNode<VT>).time) > beats ? false : EventNode.nextStartInJumpArray(node)
749
713
  },
750
714
  (node: EventStartNode) => {
751
715
  return node.next && node.next.type === NodeType.TAIL ? node.next : node;
@@ -762,12 +726,10 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
762
726
 
763
727
  }
764
728
  this.jump.updateRange(from, to);
765
- }
766
- insert() {
767
-
768
729
  }
769
730
  getNodeAt(beats: number, usePrev: boolean = false): EventStartNode<VT> {
770
- let node = this.jump?.getNodeAt(beats) as (EventStartNode<VT> | EventNodeLike<NodeType.TAIL, VT>)
731
+ let node
732
+ = this.jump?.getNodeAt(beats) as (EventStartNode<VT> | EventNodeLike<NodeType.TAIL, VT>)
771
733
  || this.head.next as (EventStartNode<VT> | EventNodeLike<NodeType.TAIL, VT>);
772
734
  if (node.type === NodeType.TAIL) {
773
735
  if (usePrev) {
@@ -776,13 +738,13 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
776
738
  // 最后一个事件节点本身具有无限延伸的特性
777
739
  return node.previous;
778
740
  }
779
- if (usePrev && TimeCalculator.toBeats(node.time) === beats) {
741
+ if (usePrev && TC.toBeats(node.time) === beats) {
780
742
  const prev = node.previous;
781
743
  if (!(prev.type === NodeType.HEAD)) {
782
744
  node = prev.previous;
783
745
  }
784
746
  }
785
- if (TimeCalculator.toBeats(node.time) > beats && beats >= 0) {
747
+ if (TC.toBeats(node.time) > beats && beats >= 0) {
786
748
  console.warn("Got a node after the given beats. This would only happen when the given beats is negative. Beats and Node:", beats, node)
787
749
  }
788
750
  return node;
@@ -790,20 +752,40 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
790
752
  getValueAt(beats: number, usePrev: boolean = false): VT {
791
753
  return this.getNodeAt(beats, usePrev).getValueAt(beats);
792
754
  }
793
- getIntegral(this: EventNodeSequence<number>, beats: number, timeCalculator: TimeCalculator) {
755
+ getFloorPositionAt(this: EventNodeSequence<number>, beats: number, timeCalculator: TimeCalculator) {
794
756
  const node: EventStartNode<number> = this.getNodeAt(beats);
795
- return node.getIntegral(beats, timeCalculator) + node.cachedIntegral
757
+ const value = node.getLocalFloorPos(beats, timeCalculator) + node.floorPosition;
758
+ if (isNaN(value)) {
759
+ debugger;
760
+ }
761
+ return value
796
762
  }
797
- updateNodesIntegralFrom(this: EventNodeSequence<number>, beats: number, timeCalculator: TimeCalculator) {
798
- let previousStartNode = this.getNodeAt(beats);
799
- previousStartNode.cachedIntegral = -previousStartNode.getIntegral(beats, timeCalculator);
800
- let totalIntegral: number = previousStartNode.cachedIntegral
801
- let endNode: EventEndNode | EventNodeLike<NodeType.TAIL>;
802
- while ((endNode = previousStartNode.next).type !== NodeType.TAIL) {
803
- const currentStartNode = endNode.next
804
- totalIntegral += previousStartNode.getFullIntegral(timeCalculator);
805
- currentStartNode.cachedIntegral = totalIntegral;
806
- previousStartNode = currentStartNode;
763
+ /**
764
+ * 更新某个速度节点之后的FP
765
+ *
766
+ * 注意,修改节点并不会影响它的FP,而是它后面的所有节点的FP,节点存的是它所在位置的FP,节点在事件头部
767
+ * @param this
768
+ * @param node
769
+ * @param tc
770
+ */
771
+ updateFloorPositionAfter(this: SpeedENS, node: EventStartNode, tc: TimeCalculator): void {
772
+ let currentFP: number;
773
+ if (node.floorPosition) {
774
+ currentFP = node.floorPosition;
775
+ } else if (node.previous.type !== NodeType.HEAD) {
776
+ const prevStart = node.previous.previous;
777
+ currentFP = prevStart.floorPosition + prevStart.getFullLocalFloorPos(tc);
778
+ } else {
779
+ currentFP = 0;
780
+ }
781
+ while (true) {
782
+ const canBeEnd = node.next;
783
+ if (canBeEnd.type === NodeType.TAIL) {
784
+ break;
785
+ }
786
+ currentFP += node.getFullLocalFloorPos(tc);
787
+ node = canBeEnd.next;
788
+ node.floorPosition = currentFP
807
789
  }
808
790
  }
809
791
  dump(): EventNodeSequenceDataKPA2<VT> {
@@ -824,8 +806,8 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
824
806
  };
825
807
  }
826
808
 
827
- getNodesFromOneAndRangeRight(node: EventStartNode<VT>, rangeRight: TimeT) {
828
- const arr = []
809
+ getNodesFromOneAndRangeRight(node: EventStartNode<VT>, rangeRight: TimeT): EventStartNode<VT>[] {
810
+ const arr: EventStartNode<VT>[] = []
829
811
  for (; !TC.gt(node.time, rangeRight); ) {
830
812
  const next = node.next;
831
813
  arr.push(node);
@@ -836,8 +818,8 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
836
818
  }
837
819
  return arr;
838
820
  }
839
- getNodesAfterOne(node: EventStartNode<VT>) {
840
- const arr = []
821
+ getNodesAfterOne(node: EventStartNode<VT>): EventStartNode<VT>[] {
822
+ const arr: EventStartNode<VT>[] = []
841
823
  while (true) {
842
824
  const next = node.next;
843
825
  if (next.type === NodeType.TAIL) {
@@ -848,6 +830,72 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
848
830
  }
849
831
  return arr;
850
832
  }
833
+ // connectLine(consumer: JudgeLine) {
834
+ // this.consumerLines.add(consumer);
835
+ // }
836
+ // disconnectLine(consumer: JudgeLine) {
837
+ // this.consumerLines.delete(consumer);
838
+ // }
839
+ /**
840
+ * 将多个事件层上的数值性事件节点序列合为一个
841
+ *
842
+ * 目前仅用于速度序列,因为就他是线性的(
843
+ *
844
+ * 会创建新序列
845
+ * @param sequences
846
+ * @throws {KPAError<ERROR_IDS.NEEDS_AT_LEAST_ONE_ENS>}
847
+ */
848
+ static mergeSequences(sequences: EventNodeSequence[]) {
849
+ if (sequences.length < 1) {
850
+ throw err.NEEDS_AT_LEAST_ONE_ENS();
851
+ }
852
+ const dest = EventNodeSequence.newSeq(EventType.speed, sequences[0].effectiveBeats);
853
+ dest.head.next.value = 0;
854
+ dest.id = sequences[0].id + "_merged";
855
+ for (const seq of sequences) {
856
+ if (seq.type !== EventType.speed) {
857
+ throw err.SEQUENCE_TYPE_NOT_CONSISTENT("speed", EventType[seq.type]);
858
+ }
859
+ const nodesToChange: [EventStartNode | EventEndNode, number][] = [];
860
+ const inserts: [toInsert: EventStartNode, targetBeats: number][] = [];
861
+ let endNode = seq.head.next.next;
862
+ // 这里是背靠背遍历,不是面对面遍历
863
+ // 我把节点关系都写这里了,通义灵码还不会看,乱给补全,罚他看114514遍
864
+ while (endNode.type !== NodeType.TAIL) {
865
+ const startNode = endNode.next;
866
+ const endTime = endNode.time;
867
+ const endBeats = TC.toBeats(endTime);
868
+ const targetStart = dest.getNodeAt(endBeats)
869
+ if (TC.eq(targetStart.time, endTime)) {
870
+ const targetEnd = targetStart.previous as EventEndNode;
871
+ nodesToChange.push([targetStart, startNode.value]);
872
+ nodesToChange.push([targetEnd, endNode.value]);
873
+ } else {
874
+ const newEnd = new EventEndNode(endTime, endNode.value + targetStart.getValueAt(endBeats));
875
+ const newStart = new EventStartNode(endTime, startNode.value + targetStart.getValueAt(endBeats));
876
+ EventNode.connect(newEnd, newStart);
877
+ inserts.push([newStart, endBeats]);
878
+ }
879
+ endNode = startNode.next;
880
+ }
881
+ dest.head.next.value += seq.head.next.value;
882
+ const len1 = nodesToChange.length;
883
+ for (let i = 0; i < len1; i++) {
884
+ const tup = nodesToChange[i];
885
+ tup[0].value += tup[1];
886
+ }
887
+ const len2 = inserts.length;
888
+ for (let i = 0; i < len2; i++) {
889
+ const tup = inserts[i];
890
+ // 这都updateJump
891
+ dest.updateJump(...EventNode.insert(tup[0], dest.getNodeAt(tup[1])));
892
+ }
893
+ }
894
+ dest.initJump();
895
+ return dest;
896
+ }
851
897
  }
852
898
 
899
+ export type SpeedENS = EventNodeSequence<number> & { type: EventType.speed };
900
+
853
901
  /// #enddeclaration