kipphi 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -3
- package/basic.ts +285 -0
- package/bpm.ts +332 -0
- package/chart.ts +418 -106
- package/chartType.schema.json +584 -0
- package/chartType2.schema.json +1107 -0
- package/chartTypes.ts +131 -30
- package/easing.ts +125 -90
- package/env.ts +208 -0
- package/evaluator.ts +106 -20
- package/event.ts +357 -255
- package/index.d.ts +3055 -0
- package/index.js +5530 -0
- package/index.ts +17 -11
- package/judgeline.ts +395 -94
- package/jumparray.ts +10 -11
- package/line.ts +246 -0
- package/macro.ts +215 -0
- package/note.ts +32 -55
- package/operation/basic.ts +285 -0
- package/operation/chart.ts +21 -0
- package/operation/event.ts +511 -0
- package/operation/index.ts +6 -0
- package/operation/line.ts +304 -0
- package/operation/macro.ts +60 -0
- package/operation/note.ts +457 -0
- package/package.json +7 -1
- package/rpeChartCompiler.ts +133 -98
- package/time.ts +35 -223
- package/tsconfig.json +2 -3
- package/util.ts +21 -1
- package/version.ts +2 -1
package/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
|
-
|
|
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 =
|
|
48
|
-
// @ts-
|
|
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 ?
|
|
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<
|
|
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
|
-
|
|
80
|
-
|
|
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<
|
|
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,
|
|
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
|
-
|
|
116
|
-
|
|
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
|
-
|
|
141
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
start.
|
|
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
|
|
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("
|
|
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) =>
|
|
268
|
-
const offset =
|
|
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 (
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
442
|
-
return timeCalculator.segmentToSeconds(
|
|
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
|
-
|
|
373
|
+
getFullLocalFloorPos(this: EventStartNode<number>, timeCalculator: TimeCalculator) {
|
|
445
374
|
if (this.next.type === NodeType.TAIL) {
|
|
446
375
|
console.log(this)
|
|
447
|
-
throw
|
|
376
|
+
throw err.CANNOT_GET_FULL_INTEGRAL_OF_FINAL_START_NODE();
|
|
448
377
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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
|
|
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
|
-
/**
|
|
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
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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
|
-
|
|
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 ?
|
|
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
|
-
|
|
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
|
-
//
|
|
592
|
-
|
|
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
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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
|
-
|
|
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
|
-
|
|
636
|
-
|
|
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>(
|
|
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
|
|
642
|
+
let lastEndTime: TimeT = [0, 0, 1];
|
|
658
643
|
for (let index = 0; index < length; index++) {
|
|
659
644
|
const event = data[index];
|
|
660
|
-
|
|
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 (
|
|
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 (
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
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
|
|
685
|
-
|
|
686
|
-
|
|
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,
|
|
689
|
-
|
|
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>(
|
|
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],
|
|
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 =
|
|
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
|
|
762
|
+
return TC.toBeats((node.next as EventEndNode<VT>).time) > beats ? false : EventNode.nextStartInJumpArray(node)
|
|
749
763
|
},
|
|
750
|
-
(node:
|
|
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
|
|
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 &&
|
|
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 (
|
|
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
|
-
|
|
805
|
+
getFloorPositionAt(this: EventNodeSequence<number>, beats: number, timeCalculator: TimeCalculator) {
|
|
794
806
|
const node: EventStartNode<number> = this.getNodeAt(beats);
|
|
795
|
-
|
|
807
|
+
const value = node.getLocalFloorPos(beats, timeCalculator) + node.floorPosition;
|
|
808
|
+
if (isNaN(value)) {
|
|
809
|
+
debugger;
|
|
810
|
+
}
|
|
811
|
+
return value
|
|
796
812
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
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
|
-
|
|
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
|