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/event.ts CHANGED
@@ -1,13 +1,19 @@
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, FinalEventStartNodeDataKPA2, EvaluatorType, EasingType } 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
+ import { EventMacro, EventMacroTime, EventMacroValue, Macroable } from "./macro";
14
+
9
15
  /// #declaration:global
10
- export class EventNodeLike<T extends NodeType, VT = number> {
16
+ export class EventNodeLike<T extends NodeType, VT extends EventValueESType = number> {
11
17
  type: T;
12
18
  /** 后一个事件节点 */
13
19
  next: [EventStartNode<VT>, null, ENOrTail<VT>][T] | null = null;
@@ -18,10 +24,10 @@ export class EventNodeLike<T extends NodeType, VT = number> {
18
24
  this.type = type;
19
25
  }
20
26
  }
21
- export type ENOrTail<VT = number> = EventNode<VT> | EventNodeLike<NodeType.TAIL, VT>;
22
- export type ENOrHead<VT = number> = EventNode<VT> | EventNodeLike<NodeType.HEAD, VT>;
23
- export type AnyEN<VT = number> = EventNode<VT> | EventNodeLike<NodeType.HEAD, VT> | EventNodeLike<NodeType.TAIL, VT>;
24
- export type EvSoE<VT = number> = EventEndNode<VT> | EventStartNode<VT>;
27
+ export type ENOrTail<VT extends EventValueESType = number> = EventNode<VT> | EventNodeLike<NodeType.TAIL, VT>;
28
+ export type ENOrHead<VT extends EventValueESType = number> = EventNode<VT> | EventNodeLike<NodeType.HEAD, VT>;
29
+ export type AnyEN<VT extends EventValueESType = number> = EventNode<VT> | EventNodeLike<NodeType.HEAD, VT> | EventNodeLike<NodeType.TAIL, VT>;
30
+ export type EvSoE<VT extends EventValueESType = number> = EventEndNode<VT> | EventStartNode<VT>;
25
31
 
26
32
  /**
27
33
  * 事件节点基类
@@ -38,14 +44,16 @@ export type EvSoE<VT = number> = EventEndNode<VT> | EventStartNode<VT>;
38
44
  * 与RPE不同的是,KPA使用两个节点来表示一个事件,而不是一个对象。
39
45
  * Different from that in RPE, KPA uses two nodes rather than one object to represent an event.
40
46
  */
41
- export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDDLE, VT> {
47
+ export abstract class EventNode<VT extends EventValueESType = number> extends EventNodeLike<NodeType.MIDDLE, VT> {
42
48
  time: TimeT;
43
49
  value: VT;
44
50
  evaluator: Evaluator<VT>;
51
+ macroValue: EventMacroValue;
52
+ linkedMacros: Set<EventMacro<Macroable>> = new Set();
45
53
  constructor(time: TimeT, value: VT) {
46
54
  super(NodeType.MIDDLE);
47
- this.time = TimeCalculator.validateIp([...time]);
48
- // @ts-ignore 不清楚什么时候会是undefined,但是留着准没错
55
+ this.time = TC.validateIp([...time]);
56
+ // @ts-expect-error 不清楚什么时候会是undefined,但是留着准没错((
49
57
  this.value = value ?? 0;
50
58
  if (typeof value === "number") {
51
59
  this.evaluator = NumericEasedEvaluator.default as unknown as Evaluator<VT>;
@@ -57,7 +65,7 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
57
65
  }
58
66
  clone(offset: TimeT): EventStartNode<VT> | EventEndNode<VT> {
59
67
  const ret = new (this.constructor as (typeof EventStartNode | typeof EventEndNode))
60
- (offset ? TimeCalculator.add(this.time, offset) : this.time, this.value);
68
+ (offset ? TC.add(this.time, offset) : this.time, this.value);
61
69
  ret.evaluator = this.evaluator;
62
70
  return ret;
63
71
  }
@@ -69,15 +77,15 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
69
77
  * @returns
70
78
  * @deprecated
71
79
  */
72
- static getEasing(data: EventDataKPA<any>, templates: TemplateEasingLib): Easing {
80
+ static getEasing(data: EventDataKPA<EventValueESType>, templates: TemplateEasingLib, notSegmented = false): Easing {
73
81
  const left = data.easingLeft;
74
82
  const right = data.easingRight;
75
- if ((left && right) && (left !== 0.0 || right !== 1.0)) {
76
- return new SegmentedEasing(EventNode.getEasing(data, templates), left, right)
83
+ if (!notSegmented && (left && right) && (left !== 0.0 || right !== 1.0)) {
84
+ return new SegmentedEasing(EventNode.getEasing(data, templates, true), left, right)
77
85
  }
78
86
  if (data.bezier) {
79
- let bp = data.bezierPoints
80
- let easing = new BezierEasing([bp[0], bp[1]], [bp[2], bp[3]]);
87
+ const bp = data.bezierPoints
88
+ const easing = new BezierEasing([bp[0], bp[1]], [bp[2], bp[3]]);
81
89
  return easing
82
90
  } else if (typeof data.easingType === "string") {
83
91
  return templates.get(data.easingType);
@@ -96,24 +104,24 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
96
104
  * @returns
97
105
  * @deprecated
98
106
  */
99
- static getEvaluator(data: EventDataKPA<any>, templates: TemplateEasingLib): Evaluator<any> {
107
+ static getEvaluator<VT extends EventValueESType>(data: EventDataKPA<VT>, templates: TemplateEasingLib, interpreteAs?: InterpreteAs): Evaluator<VT> {
100
108
  const left = data.easingLeft;
101
109
  const right = data.easingRight;
102
- const wrap = (easing: Easing) => {
110
+ const wrap = (easing: Easing): EasedEvaluator<VT> => {
103
111
  if (typeof data.start === "number") {
104
- return new NumericEasedEvaluator(easing);
112
+ return new NumericEasedEvaluator(easing) as unknown as EasedEvaluator<VT>;
105
113
  } else if (typeof data.start === "string") {
106
- return new TextEasedEvaluator(easing, data.interpreteAs);
114
+ return new TextEasedEvaluator(easing, interpreteAs) as unknown as EasedEvaluator<VT>;
107
115
  } else {
108
- return new ColorEasedEvaluator(easing);
116
+ return new ColorEasedEvaluator(easing) as unknown as EasedEvaluator<VT>;
109
117
  }
110
- }
118
+ };
111
119
  if ((left && right) && (left !== 0.0 || right !== 1.0)) {
112
120
  return wrap(new SegmentedEasing(EventNode.getEasing(data, templates), left, right))
113
121
  }
114
122
  if (data.bezier) {
115
- let bp = data.bezierPoints
116
- let easing = new BezierEasing([bp[0], bp[1]], [bp[2], bp[3]]);
123
+ const bp = data.bezierPoints
124
+ const easing = new BezierEasing([bp[0], bp[1]], [bp[2], bp[3]]);
117
125
  return wrap(easing)
118
126
  } else if (data.isParametric) {
119
127
  if (typeof data.easingType !== "string") {
@@ -137,8 +145,8 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
137
145
  * @returns
138
146
  */
139
147
  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);
148
+ const start = new EventStartNode(data.startTime, data.start)
149
+ const end = new EventEndNode(data.endTime, data.end);
142
150
  start.evaluator = EventNode.getEvaluator(data, chart.templateEasingLib);
143
151
  EventNode.connect(start, end);
144
152
  return [start, end]
@@ -161,16 +169,15 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
161
169
  interpreteAs = InterpreteAs.int;
162
170
  }
163
171
  }
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);
172
+ const start = new EventStartNode<string>(data.startTime, startValue);
173
+ const end = new EventEndNode<string>(data.endTime, endValue);
174
+ start.evaluator = EventNode.getEvaluator(data, templates, (data as unknown as EventDataKPA).interpreteAs ?? interpreteAs);
168
175
  EventNode.connect(start, end)
169
176
  return [start, end]
170
177
  }
171
- static connect<VT>(node1: EventStartNode<VT>, node2: EventEndNode<VT> | EventNodeLike<NodeType.TAIL, VT>): void
172
- static connect<VT>(node1: EventEndNode<VT> | EventNodeLike<NodeType.HEAD, VT>, node2: EventStartNode<VT>): void
173
- static connect<VT>(node1: ENOrHead<VT>, node2: ENOrTail<VT>): void {
178
+ static connect<VT extends EventValueESType>(node1: EventStartNode<VT>, node2: EventEndNode<VT> | EventNodeLike<NodeType.TAIL, VT>): void
179
+ static connect<VT extends EventValueESType>(node1: EventEndNode<VT> | EventNodeLike<NodeType.HEAD, VT>, node2: EventStartNode<VT>): void
180
+ static connect<VT extends EventValueESType>(node1: ENOrHead<VT>, node2: ENOrTail<VT>): void {
174
181
  node1.next = node2;
175
182
  node2.previous = node1;
176
183
  if (node1 && node2) {
@@ -183,7 +190,7 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
183
190
  * @param startNode
184
191
  * @returns 应该在何范围内更新跳数组
185
192
  */
186
- static removeNodePair<VT>(endNode: EventEndNode<VT>, startNode: EventStartNode<VT>): [EventStartNode<VT> | EventNodeLike<NodeType.HEAD, VT>, EventStartNode<VT> | EventNodeLike<NodeType.TAIL,VT>] {
193
+ static removeNodePair<VT extends EventValueESType>(endNode: EventEndNode<VT>, startNode: EventStartNode<VT>): [EventStartNode<VT> | EventNodeLike<NodeType.HEAD, VT>, EventStartNode<VT> | EventNodeLike<NodeType.TAIL,VT>] {
187
194
  const prev = endNode.previous;
188
195
  const next = startNode.next;
189
196
  prev.next = next;
@@ -194,10 +201,10 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
194
201
  startNode.parentSeq = null; // 每亩的东西(
195
202
  return [this.previousStartOfStart(prev), this.nextStartOfEnd(next)]
196
203
  }
197
- static insert<VT>(node: EventStartNode<VT>, tarPrev: EventStartNode<VT>): [EventNodeLike<NodeType.HEAD, VT> | EventStartNode<VT>, EventStartNode<VT> | EventNodeLike<NodeType.TAIL, VT>] {
204
+ static insert<VT extends EventValueESType>(node: EventStartNode<VT>, tarPrev: EventStartNode<VT>): [EventNodeLike<NodeType.HEAD, VT> | EventStartNode<VT>, EventStartNode<VT> | EventNodeLike<NodeType.TAIL, VT>] {
198
205
  const tarNext = tarPrev.next;
199
206
  if (node.previous.type === NodeType.HEAD) {
200
- throw new Error("Cannot insert a head node before any node");
207
+ throw err.CANNOT_INSERT_BEFORE_HEAD();
201
208
  }
202
209
  this.connect(tarPrev, node.previous);
203
210
  node.parentSeq = node.previous.parentSeq;
@@ -209,7 +216,7 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
209
216
  * @param node
210
217
  * @returns the next node if it is a tailer, otherwise the next start node
211
218
  */
212
- static nextStartOfStart<VT>(node: EventStartNode<VT>) {
219
+ static nextStartOfStart<VT extends EventValueESType>(node: EventStartNode<VT>) {
213
220
  return node.next.type === NodeType.TAIL ? node.next : node.next.next
214
221
  }
215
222
  /**
@@ -217,10 +224,10 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
217
224
  * @param node
218
225
  * @returns itself if node is a tailer, otherwise the next start node
219
226
  */
220
- static nextStartOfEnd<VT>(node: EventEndNode<VT> | EventNodeLike<NodeType.TAIL, VT>) {
227
+ static nextStartOfEnd<VT extends EventValueESType>(node: EventEndNode<VT> | EventNodeLike<NodeType.TAIL, VT>) {
221
228
  return node.type === NodeType.TAIL ? node : node.next
222
229
  }
223
- static previousStartOfStart<VT>(node: EventStartNode<VT>): EventStartNode<VT> | EventNodeLike<NodeType.HEAD, VT> {
230
+ static previousStartOfStart<VT extends EventValueESType>(node: EventStartNode<VT>): EventStartNode<VT> | EventNodeLike<NodeType.HEAD, VT> {
224
231
  return node.previous.type === NodeType.HEAD ? node.previous : node.previous.previous;
225
232
  }
226
233
  /**
@@ -228,10 +235,10 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
228
235
  * @param node
229
236
  * @returns
230
237
  */
231
- static secondPreviousStartOfEnd<VT>(node: EventEndNode<VT>): EventStartNode<VT> | EventNodeLike<NodeType.HEAD, VT> {
238
+ static secondPreviousStartOfEnd<VT extends EventValueESType>(node: EventEndNode<VT>): EventStartNode<VT> | EventNodeLike<NodeType.HEAD, VT> {
232
239
  return this.previousStartOfStart(node.previous);
233
240
  }
234
- static nextStartInJumpArray<VT>(node: EventStartNode<VT>): EventStartNode<VT> | EventNodeLike<NodeType.TAIL, VT> {
241
+ static nextStartInJumpArray<VT extends EventValueESType>(node: EventStartNode<VT>): EventStartNode<VT> | EventNodeLike<NodeType.TAIL, VT> {
235
242
  if ((node.next as EventEndNode<VT>).next.isLastStart()) {
236
243
  return node.next.next.next as EventNodeLike<NodeType.TAIL, VT>;
237
244
  } else {
@@ -243,7 +250,7 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
243
250
  * @param node
244
251
  * @returns
245
252
  */
246
- static getEndStart<VT>(node: EventStartNode<VT> | EventEndNode<VT>): [EventEndNode<VT>, EventStartNode<VT>] {
253
+ static getEndStart<VT extends EventValueESType>(node: EventStartNode<VT> | EventEndNode<VT>): [EventEndNode<VT>, EventStartNode<VT>] {
247
254
  if (node instanceof EventStartNode) {
248
255
  if (node.isFirstStart()) {
249
256
  throw new Error("Cannot get previous start node of the first start node");
@@ -253,23 +260,23 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
253
260
  return [node, node.next]
254
261
  }
255
262
  }
256
- static getStartEnd<VT>(node: EventStartNode<VT> | EventEndNode<VT>): [EventStartNode<VT>, EventEndNode<VT>] {
263
+ static getStartEnd<VT extends EventValueESType>(node: EventStartNode<VT> | EventEndNode<VT>): [EventStartNode<VT>, EventEndNode<VT>] {
257
264
  if (node instanceof EventStartNode) {
258
265
  return [node, <EventEndNode<VT>>node.next]
259
266
  } else if (node instanceof EventEndNode) {
260
267
  return [<EventStartNode<VT>>node.previous, node]
261
268
  } else {
262
- throw new Error("Invalid node type")
269
+ throw new Error("unreachable");
263
270
  }
264
271
  }
265
- static setToNewOrderedArray<VT>(dest: TimeT, set: Set<EventStartNode<VT>>): [EventStartNode<VT>[], EventStartNode<VT>[]] {
272
+ static setToNewOrderedArray<VT extends EventValueESType>(dest: TimeT, set: Set<EventStartNode<VT>>): [EventStartNode<VT>[], EventStartNode<VT>[]] {
266
273
  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)
274
+ nodes.sort((a, b) => TC.gt(a.time, b.time) ? 1 : -1);
275
+ const offset = TC.sub(dest, nodes[0].time)
269
276
  return [nodes, nodes.map(node => node.clonePair(offset))]
270
277
  }
271
278
  static belongToSequence(nodes: Set<EventStartNode>, sequence: EventNodeSequence): boolean {
272
- for (let each of nodes) {
279
+ for (const each of nodes) {
273
280
  if (each.parentSeq !== sequence) {
274
281
  return false;
275
282
  }
@@ -297,76 +304,16 @@ export abstract class EventNode<VT = number> extends EventNodeLike<NodeType.MIDD
297
304
  // #endregion
298
305
  }
299
306
 
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
- export class EventStartNode<VT = number> extends EventNode<VT> {
307
+ export class EventStartNode<VT extends EventValueESType = number> extends EventNode<VT> {
351
308
  override next: EventEndNode<VT> | EventNodeLike<NodeType.TAIL, VT>;
352
309
  override previous: EventEndNode<VT> | EventNodeLike<NodeType.HEAD, VT>;
310
+ macroTime: EventMacroTime | null = null;
353
311
  /**
354
- * 对于速度事件,从计算时的时刻到此节点的总积分
312
+ * 对于速度事件,从0时刻到此节点的总积分
355
313
  */
356
- cachedIntegral?: number;
314
+ floorPosition: number = 0;
357
315
  constructor(time: TimeT, value: VT) {
358
316
  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
317
  }
371
318
  override parentSeq: EventNodeSequence<VT>;
372
319
  /**
@@ -381,74 +328,56 @@ export class EventStartNode<VT = number> extends EventNode<VT> {
381
328
  end: endNode.value,
382
329
  startTime: this.time,
383
330
  endTime: endNode.time,
384
- evaluator: this.evaluator.dump(),
331
+ evaluator: this.evaluator.dumpFor(this),
332
+ macroStart: this.macroValue?.dumpForNode(this),
333
+ macroEnd: this.macroValue?.dumpForNode(endNode),
334
+ macroStartTime: this.macroTime?.dumpForNode(this),
335
+ // 没有macroEndTime
336
+ startLinkedMacro: [...this.linkedMacros].map(macro => macro.dumpLinkForNode(this)),
337
+ endLinkedMacro: [...endNode.linkedMacros].map(macro => macro.dumpLinkForNode(endNode))
385
338
  }
386
339
  }
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;
340
+ dumpAsFinal(): FinalEventStartNodeDataKPA2<VT> {
396
341
  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
342
  start: this.value,
412
343
  startTime: this.time,
344
+ evaluator: this.evaluator.dumpFor(this),
345
+ macro: this.macroValue?.dumpForNode(this),
346
+ macroTime: this.macroTime?.dumpForNode(this),
347
+ linkedMacro: [...this.linkedMacros].map(macro => macro.dumpLinkForNode(this)),
413
348
  }
414
- }//*/
415
- interpretedAs: InterpreteAs = InterpreteAs.str;
349
+ }
416
350
  getValueAt(beats: number): VT {
417
351
  // 除了尾部的开始节点,其他都有下个节点
418
352
  // 钩定型缓动也有
419
353
  if (this.next.type === NodeType.TAIL) {
420
354
  return this.value;
421
355
  }
422
- return this.evaluator.eval(this, beats);
356
+ return this.evaluator.eval(this as NonLastStartNode<VT>, beats);
423
357
  }
424
- private getValueFn: (current: number, timeDelta: number, value: VT, nextVal: VT, easing: Easing, interpreteAs?: InterpreteAs) => VT
425
358
  getSpeedValueAt(this: EventStartNode<number>, beats: number) {
426
359
  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
360
+ return this.value;
435
361
  }
362
+ const timeDelta = TC.getDelta(this.next.time, this.time);
363
+ const valueDelta = this.next.value - this.value;
364
+ const current = beats - TC.toBeats(this.time);
436
365
  return this.value + linearEasing.getValue(current / timeDelta) * valueDelta;
437
366
  }
438
367
  /**
439
368
  * 积分获取位移
440
369
  */
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
370
+ getLocalFloorPos(this: EventStartNode<number>, beats: number, timeCalculator: TimeCalculator) {
371
+ return timeCalculator.segmentToSeconds(TC.toBeats(this.time), beats) * (this.value + this.getSpeedValueAt(beats)) / 2 * 120 // 每单位120px
443
372
  }
444
- getFullIntegral(this: EventStartNode<number>, timeCalculator: TimeCalculator) {
373
+ getFullLocalFloorPos(this: EventStartNode<number>, timeCalculator: TimeCalculator) {
445
374
  if (this.next.type === NodeType.TAIL) {
446
375
  console.log(this)
447
- throw new Error("getFullIntegral不可用于尾部节点")
376
+ throw err.CANNOT_GET_FULL_INTEGRAL_OF_FINAL_START_NODE();
448
377
  }
449
- let end = this.next;
450
- let endBeats = TimeCalculator.toBeats(end.time)
451
- let startBeats = TimeCalculator.toBeats(this.time)
378
+ const end = this.next;
379
+ const endBeats = TC.toBeats(end.time)
380
+ const startBeats = TC.toBeats(this.time)
452
381
  // 原来这里写反了,气死偶咧!
453
382
  return timeCalculator.segmentToSeconds(startBeats, endBeats) * (this.value + end.value) / 2 * 120
454
383
  }
@@ -458,6 +387,9 @@ export class EventStartNode<VT = number> extends EventNode<VT> {
458
387
  isLastStart() {
459
388
  return this.next && this.next.type === NodeType.TAIL
460
389
  }
390
+ isSpeed() {
391
+ return this.parentSeq.type === EventType.speed;
392
+ }
461
393
  override clone(offset?: TimeT): EventStartNode<VT> {
462
394
  return super.clone(offset) as EventStartNode<VT>;
463
395
  };
@@ -467,32 +399,15 @@ export class EventStartNode<VT = number> extends EventNode<VT> {
467
399
  EventNode.connect(endNode, startNode);
468
400
  return startNode;
469
401
  };
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
402
  }
492
403
 
493
- export class EventEndNode<VT = number> extends EventNode<VT> {
404
+ export type NonLastStartNode<VT extends EventValueESType> = EventStartNode<VT> & {next: EventEndNode<VT>}
405
+
406
+ export class EventEndNode<VT extends EventValueESType = number> extends EventNode<VT> {
494
407
  override next!: EventStartNode<VT>;
495
408
  override previous!: EventStartNode<VT>;
409
+
410
+ // @ts-expect-error 这里没有类型安全问题
496
411
  override get parentSeq(): EventNodeSequence<VT> {return this.previous?.parentSeq || null}
497
412
  override set parentSeq(_parent: EventNodeSequence<VT>) {}
498
413
  constructor(time: TimeT, value: VT) {
@@ -507,6 +422,12 @@ export class EventEndNode<VT = number> extends EventNode<VT> {
507
422
  }
508
423
 
509
424
 
425
+ export enum Monotonicity {
426
+ increasing,
427
+ decreasing,
428
+ swinging
429
+ }
430
+
510
431
  /**
511
432
  * 为一个链表结构。会有一个数组进行快跳。
512
433
  * is the list of event nodes, but not purely start nodes.
@@ -533,32 +454,52 @@ export class EventEndNode<VT = number> extends EventNode<VT> {
533
454
  * 插入或删除节点时,需要更新跳数组。
534
455
  * Remember to update the jump array when inserting or deleting nodes.
535
456
  */
536
- export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
457
+ export class EventNodeSequence<VT extends EventValueESType = number> { // 泛型的传染性这一块
458
+ /**
459
+ * @deprecated 谱面属性未实装,以后有必要的时候会添加为其赋值的逻辑
460
+ */
537
461
  chart: Chart;
538
- /** id follows the format `#${lineid}.${layerid}.${typename}` by default */
462
+ /** 标识名默认遵循`#${lineid}.${layerid}.${typename}`格式
463
+ * id follows the format `#${lineid}.${layerid}.${typename}` by default
464
+ */
539
465
  id: string;
540
- /** has no time or value */
541
466
  head: EventNodeLike<NodeType.HEAD, VT>;
542
- /** has no time or value */
543
467
  tail: EventNodeLike<NodeType.TAIL, VT>;
544
468
  jump?: JumpArray<AnyEN<VT>>;
545
469
  listLength: number;
546
470
  /** 一定是二的幂,避免浮点误差 */
547
471
  jumpAverageBeats: number;
548
- // nodes: EventNode[];
549
- // startNodes: EventStartNode[];
550
- // endNodes: EventEndNode[];
551
- // eventTime: Float64Array;
472
+
473
+ /**
474
+ * 用于速度事件优化
475
+ */
476
+ monotonicity: Monotonicity = Monotonicity.swinging;
477
+
478
+ /**
479
+ * 使用了该序列的判定线,当前仅用于速度事件对fp的更新
480
+ */
481
+ consumerLines = new Set<JudgeLine>;
482
+
552
483
  constructor(public type: EventType, public effectiveBeats: number) {
553
484
  this.head = new EventNodeLike(NodeType.HEAD);
554
485
  this.tail = new EventNodeLike(NodeType.TAIL);
555
486
  this.head.parentSeq = this.tail.parentSeq = this;
556
487
  this.listLength = 1;
557
- // this.head = this.tail = new EventStartNode([0, 0, 0], 0)
558
- // this.nodes = [];
559
- // this.startNodes = [];
560
- // this.endNodes = [];
561
488
  }
489
+ /**
490
+ * 获取指定事件类型的事件默认值。
491
+ * Get the default value of the specified event type.
492
+ *
493
+ * 对于文字事件,默认值为空字符串。
494
+ *
495
+ * 对于缩放事件,默认值为1.0。
496
+ *
497
+ * 对于颜色事件,默认值为黑色([0, 0, 0])。
498
+ *
499
+ * 对于速度事件,默认值为10(1200像素/秒)。
500
+ *
501
+ * 对于一般数值事件,默认值为0。
502
+ */
562
503
  static getDefaultValueFromEventType(type: EventType) {
563
504
 
564
505
  return type === EventType.speed ? 10 :
@@ -567,15 +508,26 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
567
508
  type === EventType.color ? [0, 0, 0] :
568
509
  0
569
510
  }
570
- static fromRPEJSON<T extends EventType, VT = number>(type: T, data: EventDataRPELike<VT>[], chart: Chart, endValue?: number) {
511
+ /**
512
+ * 从RPEJSON数据创建一个事件序列。
513
+ *
514
+ * KPAJSON 1.x也会使用此接口
515
+ * @param type 事件类型(不是数值类型,也不是数值ECMAScript类型)
516
+ * @param data 事件数据的数组
517
+ * @param chart
518
+ * @param pos 当前事件序列应当具有什么ID(用于报错和警告,外部调用可以写空字符串)
519
+ * @param endValue 结束值(RPEJSON没有)
520
+ * @returns
521
+ */
522
+ static fromRPEJSON<T extends EventType, VT extends EventValueESType = number>(type: T, data: EventDataRPELike<VT>[], chart: Chart, pos: string, endValue?: number) {
571
523
  const {templateEasingLib: templates} = chart
572
524
  const length = data.length;
573
525
  // const isSpeed = type === EventType.Speed;
574
526
  // console.log(isSpeed)
575
- const seq = new EventNodeSequence<VT>(type, type === EventType.easing ? TimeCalculator.toBeats(data[length - 1].endTime) : chart.effectiveBeats);
527
+ const seq = new EventNodeSequence<VT>(type, type === EventType.easing ? TC.toBeats(data[length - 1].endTime) : chart.effectiveBeats);
576
528
  let listLength = length;
577
529
  let lastEnd: EventEndNode<VT> | EventNodeLike<NodeType.HEAD, VT> = seq.head;
578
- // 如果第一个事件不从0时间开始,那么添加一对面对面节点来垫背
530
+ // 如果第一个事件不从0时间开始,那么添加一对面对面节点来垫底
579
531
  if (data[0] && TC.ne(data[0].startTime, [0, 0, 1])) {
580
532
  const value = data[0].start
581
533
  const start = new EventStartNode<VT>([0, 0, 1], value as VT);
@@ -585,59 +537,90 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
585
537
  lastEnd = end;
586
538
  }
587
539
 
588
- let lastIntegral: number = 0;
540
+ // 当前仅用于检查时间递增
541
+ let lastEndTime: TimeT = [0, 0, 1];
542
+
543
+ // 读取事件列表
589
544
  for (let index = 0; index < length; index++) {
590
545
  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>];
546
+ if (TC.lt(event.startTime, lastEndTime)) { // event.startTime < lastEndTime
547
+ err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}] and the previous`).warn()
548
+ }
549
+ if (!TC.lt(event.startTime, event.endTime)) {
550
+ err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}]`).warn()
551
+ }
552
+ lastEndTime = event.endTime;
553
+ const [start, end] = (type === EventType.text
554
+ ? EventNode.fromTextEvent(event as EventDataRPELike<string>, templates)
555
+ : EventNode.fromEvent(event as EventDataRPELike<number | RGB>, chart)) as unknown as [EventStartNode<VT>, EventEndNode<VT>];
556
+ // 刚开始时,上个节点是头
593
557
  if (lastEnd.type === NodeType.HEAD) {
594
558
  EventNode.connect(lastEnd, start)
595
559
  // 如果上一个是钩定事件,那么一块捋平
596
560
  } else if (lastEnd.value === lastEnd.previous.value && lastEnd.previous.evaluator instanceof EasedEvaluator) {
597
561
  lastEnd.time = start.time
598
562
  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);
563
+
564
+ // 如果上一个事件的结束时间和下一个事件的开始时间不一样,那么中间插入一对面对面节点
565
+ } else if (TC.toBeats(lastEnd.time) !== TC.toBeats(start.time)) {
566
+ const val = lastEnd.value;
567
+ const midStart = new EventStartNode(lastEnd.time, val);
568
+ const midEnd = new EventEndNode(start.time, val);
603
569
  midStart.evaluator = lastEnd.previous.evaluator;
604
570
  EventNode.connect(lastEnd, midStart);
605
571
  EventNode.connect(midStart, midEnd);
606
572
  EventNode.connect(midEnd, start)
607
- // seq.startNodes.push(midStart);
608
- // seq.endNodes.push(midEnd);
609
573
  listLength++;
574
+ // 最后应该就是上一个事件和这个事件首尾相接的情况
610
575
  } else {
611
576
 
612
577
  EventNode.connect(lastEnd, start)
613
578
  }
614
579
 
615
- // seq.startNodes.push(start);
616
- // seq.endNodes.push(end);
617
580
  lastEnd = end;
618
- // seq.nodes.push(start, end);
619
581
  }
620
582
  const last = lastEnd;
621
583
  const tail = new EventStartNode(
622
584
  last.type === NodeType.HEAD ? [0, 0, 1] : last.time,
623
- last.type === NodeType.HEAD ? endValue : last.value
585
+ endValue ?? (last as EventEndNode<VT>).value
624
586
  );
625
587
  EventNode.connect(last, tail);
626
588
  // last can be a header, in which case easing is undefined.
627
589
  // then we use the easing that initialized in the EventStartNode constructor.
628
590
  tail.evaluator = last.previous?.evaluator ?? tail.evaluator;
629
- tail.cachedIntegral = lastIntegral
630
591
  EventNode.connect(tail, seq.tail)
631
592
  seq.listLength = listLength;
632
593
  seq.initJump();
594
+ if (type === EventType.speed) {
595
+ (seq as SpeedENS).updateFloorPositionAfter((seq as SpeedENS).head.next, chart.timeCalculator)
596
+ }
633
597
  return seq;
634
598
  }
635
- static fromKPA2JSON<T extends EventType, VT extends EventValueESType = number>(type: T, data: EventDataKPA2<VT>[], chart: Chart, endValue?: number) {
636
- const {templateEasingLib: templates} = chart
599
+ /**
600
+ * 从KPAJSON 2.x数据创建一个事件序列。
601
+ * @param type 事件类型(不是数值类型,也不是数值ECMAScript类型)
602
+ * @param data 事件数据的数组
603
+ * @param chart
604
+ * @param pos 当前事件序列应当具有什么ID(用于报错和警告)
605
+ * @param finalNodeData 最终节点数据
606
+ * @returns
607
+ */
608
+ static fromKPA2JSON<T extends EventType, VT extends EventValueESType = number>(
609
+ type: T,
610
+ data: EventDataKPA2<VT>[],
611
+ chart: Chart,
612
+ pos: string,
613
+ finalNodeData: FinalEventStartNodeDataKPA2<VT>
614
+ )
615
+ {
637
616
  const length = data.length;
638
617
  // const isSpeed = type === EventType.Speed;
639
618
  // console.log(isSpeed)
640
- const seq = new EventNodeSequence<VT>(type, type === EventType.easing ? TimeCalculator.toBeats(data[length - 1].endTime) : chart.effectiveBeats);
619
+ const seq = new EventNodeSequence<VT>(
620
+ type,
621
+ type === EventType.easing
622
+ ? TC.toBeats(data[length - 1].endTime)
623
+ : chart.effectiveBeats);
641
624
  let listLength = length;
642
625
  let lastEnd: EventEndNode<VT> | EventNodeLike<NodeType.HEAD, VT> = seq.head;
643
626
  // 如果第一个事件不从0时间开始,那么添加一对面对面节点来垫背
@@ -650,24 +633,38 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
650
633
  lastEnd = end;
651
634
  }
652
635
 
653
- const valueType = type === EventType.color ? EventValueType.color
636
+ const valueType = (type === EventType.color ? EventValueType.color
654
637
  : type === EventType.text ? EventValueType.text
655
- : EventValueType.numeric;
638
+ : EventValueType.numeric) as EventValueTypeOfType<VT>;
639
+
640
+
656
641
 
657
- let lastIntegral: number = 0;
642
+ let lastEndTime: TimeT = [0, 0, 1];
658
643
  for (let index = 0; index < length; index++) {
659
644
  const event = data[index];
660
- let [start, end] = chart.createEventFromData<VT>(event, valueType);
645
+ const [start, end] = chart.createEventFromData<VT>(event, valueType, `${pos}.events[${index}]`);
646
+ // 从前面复制了,复用性减一
647
+ // KPA2没有更改RPE的按事件存储的机制。
648
+ if (TC.lt(event.startTime, lastEndTime)) { // event.startTime < lastEndTime
649
+ err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}] and the previous`).warn()
650
+ }
651
+ if (!TC.lt(event.startTime, event.endTime)) {
652
+ err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}]`).warn()
653
+ }
654
+ lastEndTime = event.endTime;
661
655
  if (lastEnd.type === NodeType.HEAD) {
662
656
  EventNode.connect(lastEnd, start)
663
657
  // 如果上一个是钩定事件,那么一块捋平
664
- } else if (lastEnd.value === lastEnd.previous.value && lastEnd.previous.evaluator instanceof EasedEvaluator) {
658
+ } else if (
659
+ lastEnd.value === lastEnd.previous.value
660
+ && lastEnd.previous.evaluator instanceof EasedEvaluator
661
+ ) {
665
662
  lastEnd.time = start.time
666
663
  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);
664
+ } else if (TC.eq(lastEnd.time, start.time)) {
665
+ const val = lastEnd.value;
666
+ const midStart = new EventStartNode(lastEnd.time, val);
667
+ const midEnd = new EventEndNode(start.time, val);
671
668
  midStart.evaluator = lastEnd.previous.evaluator;
672
669
  EventNode.connect(lastEnd, midStart);
673
670
  EventNode.connect(midStart, midEnd);
@@ -681,18 +678,30 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
681
678
  lastEnd = end;
682
679
  }
683
680
  const last = lastEnd;
684
- const tail = new EventStartNode(
685
- last.type === NodeType.HEAD ? [0, 0, 1] : last.time,
686
- last.type === NodeType.HEAD ? endValue : last.value
681
+ const final = chart.createFinalEventStartNodeFromData(
682
+ finalNodeData || {
683
+ // @ts-expect-error 简化写法
684
+ start: last.value ?? this.getDefaultValueFromEventType(type),
685
+ // @ts-expect-error 极其边缘情况,finalNodeData只有在内测的谱面里面才可能为null,且last一般都有值
686
+ startTime: last.time,
687
+ evaluator: {
688
+ type: EvaluatorType.eased,
689
+ easing: {
690
+ type: EasingType.normal,
691
+ identifier: linearEasing.rpeId
692
+ }
693
+ }
694
+ } satisfies FinalEventStartNodeDataKPA2<any>,
695
+ valueType,
696
+ pos + ".finalNode"
687
697
  );
688
- EventNode.connect(last, tail);
689
- // last can be a header, in which case easing is undefined.
690
- // then we use the easing that initialized in the EventStartNode constructor.
691
- tail.evaluator = last.previous?.evaluator ?? tail.evaluator;
692
- tail.cachedIntegral = lastIntegral
693
- EventNode.connect(tail, seq.tail)
698
+ EventNode.connect(last, final);
699
+ EventNode.connect(final, seq.tail)
694
700
  seq.listLength = listLength;
695
701
  seq.initJump();
702
+ if (type === EventType.speed) {
703
+ (seq as SpeedENS).updateFloorPositionAfter((seq as SpeedENS).head.next, chart.timeCalculator)
704
+ }
696
705
  return seq;
697
706
  }
698
707
  /**
@@ -702,11 +711,16 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
702
711
  * @param effectiveBeats
703
712
  * @returns
704
713
  */
705
- static newSeq<T extends EventType>(type: T, effectiveBeats: number): EventNodeSequence<ValueTypeOfEventType<T>> {
714
+ static newSeq<T extends EventType>(
715
+ type: T,
716
+ effectiveBeats: number
717
+ ): EventNodeSequence<ValueTypeOfEventType<T>>
718
+ {
706
719
  type V = ValueTypeOfEventType<T>
707
720
  const sequence = new EventNodeSequence<V>(type, effectiveBeats);
708
721
  const node = new EventStartNode<V>(
709
- [0, 0, 1], EventNodeSequence.getDefaultValueFromEventType(type) as V
722
+ [0, 0, 1],
723
+ EventNodeSequence.getDefaultValueFromEventType(type) as V
710
724
  );
711
725
  EventNode.connect(sequence.head, node)
712
726
  EventNode.connect(node, sequence.tail)
@@ -736,7 +750,7 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
736
750
  return [0, node.next]
737
751
  }
738
752
  const endNode = (node as EventStartNode<VT>).next as EventEndNode<VT>;
739
- const time = TimeCalculator.toBeats(endNode.time);
753
+ const time = TC.toBeats(endNode.time);
740
754
  const nextNode = endNode.next;
741
755
  if (nextNode.next.type === NodeType.TAIL) {
742
756
  return [time, nextNode.next] // Tailer代替最后一个StartNode去占位
@@ -745,9 +759,9 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
745
759
  }
746
760
  },
747
761
  (node: EventStartNode<VT>, beats: number) => {
748
- return TimeCalculator.toBeats((node.next as EventEndNode<VT>).time) > beats ? false : EventNode.nextStartInJumpArray(node)
762
+ return TC.toBeats((node.next as EventEndNode<VT>).time) > beats ? false : EventNode.nextStartInJumpArray(node)
749
763
  },
750
- (node: EventStartNode) => {
764
+ (node: AnyEN<VT>) => {
751
765
  return node.next && node.next.type === NodeType.TAIL ? node.next : node;
752
766
  }
753
767
  /*(node: EventStartNode) => {
@@ -762,12 +776,10 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
762
776
 
763
777
  }
764
778
  this.jump.updateRange(from, to);
765
- }
766
- insert() {
767
-
768
779
  }
769
780
  getNodeAt(beats: number, usePrev: boolean = false): EventStartNode<VT> {
770
- let node = this.jump?.getNodeAt(beats) as (EventStartNode<VT> | EventNodeLike<NodeType.TAIL, VT>)
781
+ let node
782
+ = this.jump?.getNodeAt(beats) as (EventStartNode<VT> | EventNodeLike<NodeType.TAIL, VT>)
771
783
  || this.head.next as (EventStartNode<VT> | EventNodeLike<NodeType.TAIL, VT>);
772
784
  if (node.type === NodeType.TAIL) {
773
785
  if (usePrev) {
@@ -776,13 +788,13 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
776
788
  // 最后一个事件节点本身具有无限延伸的特性
777
789
  return node.previous;
778
790
  }
779
- if (usePrev && TimeCalculator.toBeats(node.time) === beats) {
791
+ if (usePrev && TC.toBeats(node.time) === beats) {
780
792
  const prev = node.previous;
781
793
  if (!(prev.type === NodeType.HEAD)) {
782
794
  node = prev.previous;
783
795
  }
784
796
  }
785
- if (TimeCalculator.toBeats(node.time) > beats && beats >= 0) {
797
+ if (TC.toBeats(node.time) > beats && beats >= 0) {
786
798
  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
799
  }
788
800
  return node;
@@ -790,20 +802,44 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
790
802
  getValueAt(beats: number, usePrev: boolean = false): VT {
791
803
  return this.getNodeAt(beats, usePrev).getValueAt(beats);
792
804
  }
793
- getIntegral(this: EventNodeSequence<number>, beats: number, timeCalculator: TimeCalculator) {
805
+ getFloorPositionAt(this: EventNodeSequence<number>, beats: number, timeCalculator: TimeCalculator) {
794
806
  const node: EventStartNode<number> = this.getNodeAt(beats);
795
- return node.getIntegral(beats, timeCalculator) + node.cachedIntegral
807
+ const value = node.getLocalFloorPos(beats, timeCalculator) + node.floorPosition;
808
+ if (isNaN(value)) {
809
+ debugger;
810
+ }
811
+ return value
796
812
  }
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;
813
+ /**
814
+ * 更新某个速度节点之后的FP
815
+ *
816
+ * 注意,修改节点并不会影响它的FP,而是它后面的所有节点的FP,节点存的是它所在位置的FP,节点在事件头部
817
+ * @param this
818
+ * @param node
819
+ * @param tc
820
+ *
821
+ * @example
822
+ * node.value += 1;
823
+ * (node.parentSequence as SpeedENS).updateFloorPositionAfter(node, tc);
824
+ */
825
+ updateFloorPositionAfter(this: SpeedENS, node: EventStartNode, tc: TimeCalculator): void {
826
+ let currentFP: number;
827
+ if (node.floorPosition) {
828
+ currentFP = node.floorPosition;
829
+ } else if (node.previous.type !== NodeType.HEAD) {
830
+ const prevStart = node.previous.previous;
831
+ currentFP = prevStart.floorPosition + prevStart.getFullLocalFloorPos(tc);
832
+ } else {
833
+ currentFP = 0;
834
+ }
835
+ while (true) {
836
+ const canBeEnd = node.next;
837
+ if (canBeEnd.type === NodeType.TAIL) {
838
+ break;
839
+ }
840
+ currentFP += node.getFullLocalFloorPos(tc);
841
+ node = canBeEnd.next;
842
+ node.floorPosition = currentFP
807
843
  }
808
844
  }
809
845
  dump(): EventNodeSequenceDataKPA2<VT> {
@@ -820,12 +856,12 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
820
856
  type: this.type,
821
857
  events: nodes,
822
858
  id: this.id,
823
- endValue: currentNode.value
859
+ final: currentNode.dumpAsFinal()
824
860
  };
825
861
  }
826
862
 
827
- getNodesFromOneAndRangeRight(node: EventStartNode<VT>, rangeRight: TimeT) {
828
- const arr = []
863
+ getNodesFromOneAndRangeRight(node: EventStartNode<VT>, rangeRight: TimeT): EventStartNode<VT>[] {
864
+ const arr: EventStartNode<VT>[] = []
829
865
  for (; !TC.gt(node.time, rangeRight); ) {
830
866
  const next = node.next;
831
867
  arr.push(node);
@@ -836,8 +872,8 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
836
872
  }
837
873
  return arr;
838
874
  }
839
- getNodesAfterOne(node: EventStartNode<VT>) {
840
- const arr = []
875
+ getNodesAfterOne(node: EventStartNode<VT>): EventStartNode<VT>[] {
876
+ const arr: EventStartNode<VT>[] = []
841
877
  while (true) {
842
878
  const next = node.next;
843
879
  if (next.type === NodeType.TAIL) {
@@ -848,6 +884,72 @@ export class EventNodeSequence<VT = number> { // 泛型的传染性这一块
848
884
  }
849
885
  return arr;
850
886
  }
887
+ // connectLine(consumer: JudgeLine) {
888
+ // this.consumerLines.add(consumer);
889
+ // }
890
+ // disconnectLine(consumer: JudgeLine) {
891
+ // this.consumerLines.delete(consumer);
892
+ // }
893
+ /**
894
+ * 将多个事件层上的数值性事件节点序列合为一个
895
+ *
896
+ * 目前仅用于速度序列,因为就他是线性的(
897
+ *
898
+ * 会创建新序列
899
+ * @param sequences
900
+ * @throws {KPAError<ERROR_IDS.NEEDS_AT_LEAST_ONE_ENS>}
901
+ */
902
+ static mergeSequences(sequences: EventNodeSequence[]) {
903
+ if (sequences.length < 1) {
904
+ throw err.NEEDS_AT_LEAST_ONE_ENS();
905
+ }
906
+ const dest = EventNodeSequence.newSeq(EventType.speed, sequences[0].effectiveBeats);
907
+ dest.head.next.value = 0;
908
+ dest.id = sequences[0].id + "_merged";
909
+ for (const seq of sequences) {
910
+ if (seq.type !== EventType.speed) {
911
+ throw err.SEQUENCE_TYPE_NOT_CONSISTENT("speed", EventType[seq.type]);
912
+ }
913
+ const nodesToChange: [EventStartNode | EventEndNode, number][] = [];
914
+ const inserts: [toInsert: EventStartNode, targetBeats: number][] = [];
915
+ let endNode = seq.head.next.next;
916
+ // 这里是背靠背遍历,不是面对面遍历
917
+ // 我把节点关系都写这里了,通义灵码还不会看,乱给补全,罚他看114514遍
918
+ while (endNode.type !== NodeType.TAIL) {
919
+ const startNode = endNode.next;
920
+ const endTime = endNode.time;
921
+ const endBeats = TC.toBeats(endTime);
922
+ const targetStart = dest.getNodeAt(endBeats)
923
+ if (TC.eq(targetStart.time, endTime)) {
924
+ const targetEnd = targetStart.previous as EventEndNode;
925
+ nodesToChange.push([targetStart, startNode.value]);
926
+ nodesToChange.push([targetEnd, endNode.value]);
927
+ } else {
928
+ const newEnd = new EventEndNode(endTime, endNode.value + targetStart.getValueAt(endBeats));
929
+ const newStart = new EventStartNode(endTime, startNode.value + targetStart.getValueAt(endBeats));
930
+ EventNode.connect(newEnd, newStart);
931
+ inserts.push([newStart, endBeats]);
932
+ }
933
+ endNode = startNode.next;
934
+ }
935
+ dest.head.next.value += seq.head.next.value;
936
+ const len1 = nodesToChange.length;
937
+ for (let i = 0; i < len1; i++) {
938
+ const tup = nodesToChange[i];
939
+ tup[0].value += tup[1];
940
+ }
941
+ const len2 = inserts.length;
942
+ for (let i = 0; i < len2; i++) {
943
+ const tup = inserts[i];
944
+ // 这都updateJump
945
+ dest.updateJump(...EventNode.insert(tup[0], dest.getNodeAt(tup[1])));
946
+ }
947
+ }
948
+ dest.initJump();
949
+ return dest;
950
+ }
851
951
  }
852
952
 
953
+ export type SpeedENS = EventNodeSequence<number> & { type: EventType.speed };
954
+
853
955
  /// #enddeclaration