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
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { Operation, ComplexOperation, UnionOperation } from "./basic";
|
|
2
|
+
|
|
3
|
+
import TC from "../time";
|
|
4
|
+
import { NoteType, TimeT } from "../chartTypes";
|
|
5
|
+
import { JudgeLine } from "../judgeline";
|
|
6
|
+
import { Note, notePropTypes, NoteNode, HNList, NNList } from "../note";
|
|
7
|
+
import { checkType } from "../util";
|
|
8
|
+
import { err } from "../env";
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
type NotePropNamePhiZone = "judgeSize" | "tint" | "tintHitEffects";
|
|
12
|
+
|
|
13
|
+
export type NotePropName = "speed" | "type" | "positionX" | "startTime" | "endTime" | "alpha" | "size" | "visibleBeats" | "yOffset" | "above" | "isFake" | NotePropNamePhiZone;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 修改 Note 的任意属性值
|
|
17
|
+
* @example
|
|
18
|
+
* // 修改 note 的 above 属性
|
|
19
|
+
* operationList.do(new NotePropChangeOperation(note, "above", true));
|
|
20
|
+
* // 修改 note 的 speed 属性
|
|
21
|
+
* // 一般不会这样做,因为修改 note 的 speed 需要把note换到另一个序列(`NNList`)中
|
|
22
|
+
* // 应使用NoteSpeedChangeOperation。YOffset也是同样的道理。
|
|
23
|
+
* operationList.do(new NotePropChangeOperation(note, "speed", 1.5));
|
|
24
|
+
* @template T - 要修改的属性名,必须是 `NotePropName` 类型
|
|
25
|
+
*/
|
|
26
|
+
export class NotePropChangeOperation<T extends NotePropName> extends Operation {
|
|
27
|
+
field: T;
|
|
28
|
+
note: Note;
|
|
29
|
+
previousValue: Note[T]
|
|
30
|
+
value: Note[T];
|
|
31
|
+
updatesEditor = true;
|
|
32
|
+
constructor(note: Note, field: T, value: Note[T]) {
|
|
33
|
+
super()
|
|
34
|
+
this.field = field
|
|
35
|
+
this.note = note;
|
|
36
|
+
this.value = value;
|
|
37
|
+
if (!checkType(value, notePropTypes[field])) {
|
|
38
|
+
throw err.INVALID_NOTE_PROP_TYPE(field, value, notePropTypes[field]);
|
|
39
|
+
}
|
|
40
|
+
this.previousValue = note[field]
|
|
41
|
+
if (value === note[field]) {
|
|
42
|
+
this.ineffective = true
|
|
43
|
+
} else if (field === "isFake") {
|
|
44
|
+
this.comboDelta = value ? -1 : 1;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
do() {
|
|
48
|
+
this.note[this.field] = this.value
|
|
49
|
+
}
|
|
50
|
+
undo() {
|
|
51
|
+
this.note[this.field] = this.previousValue
|
|
52
|
+
}
|
|
53
|
+
override rewrite(operation: NotePropChangeOperation<T>): boolean {
|
|
54
|
+
if (operation.note === this.note && this.field === operation.field) {
|
|
55
|
+
this.value = operation.value;
|
|
56
|
+
this.note[this.field] = operation.value
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export class NoteRemoveOperation extends Operation {
|
|
63
|
+
noteNode: NoteNode;
|
|
64
|
+
note: Note;
|
|
65
|
+
isHold: boolean;
|
|
66
|
+
constructor(note: Note) {
|
|
67
|
+
super()
|
|
68
|
+
this.note = note // In memory of forgettting to add this(
|
|
69
|
+
this.isHold = note.type === NoteType.hold;
|
|
70
|
+
if (!note.parentNode) {
|
|
71
|
+
this.ineffective = true
|
|
72
|
+
} else {
|
|
73
|
+
this.noteNode = note.parentNode
|
|
74
|
+
}
|
|
75
|
+
// 移除假Note不改变物量
|
|
76
|
+
// 这里,一般没有人会在修改一个isFake值之后删除它,因此一般不用懒操作
|
|
77
|
+
this.comboDelta = note.isFake ? 0 : -1;
|
|
78
|
+
}
|
|
79
|
+
do() {
|
|
80
|
+
const {note, noteNode} = this;
|
|
81
|
+
noteNode.remove(note);
|
|
82
|
+
const needsUpdate = this.isHold && TC.lt(noteNode.endTime, note.endTime)
|
|
83
|
+
if (needsUpdate) {
|
|
84
|
+
const endBeats = TC.toBeats(note.endTime);
|
|
85
|
+
const tailJump = (noteNode.parentSeq as HNList).holdTailJump;
|
|
86
|
+
const updateFrom = tailJump.header
|
|
87
|
+
const updateTo = tailJump.tailer;
|
|
88
|
+
// tailJump.getPreviousOf(noteNode, endBeats);
|
|
89
|
+
tailJump.updateRange(updateFrom, noteNode.next);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
undo() {
|
|
93
|
+
const {note, noteNode} = this;
|
|
94
|
+
const needsUpdate = this.isHold && TC.lt(noteNode.endTime, note.endTime);
|
|
95
|
+
if (needsUpdate) {
|
|
96
|
+
const endBeats = TC.toBeats(note.endTime);
|
|
97
|
+
const tailJump = (noteNode.parentSeq as HNList).holdTailJump;
|
|
98
|
+
const updateFrom = tailJump.getNodeAt(endBeats).previous;
|
|
99
|
+
noteNode.add(note)
|
|
100
|
+
tailJump.updateRange(updateFrom, noteNode.next);
|
|
101
|
+
} else {
|
|
102
|
+
noteNode.add(note);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 删除一个 note(语义层面的删除)
|
|
109
|
+
* 从语义上删除 Note 要用这个操作
|
|
110
|
+
* 注意:实际移除由 NoteRemoveOperation 完成,NoteDeleteOperation 仅负责更新编辑器
|
|
111
|
+
* @example
|
|
112
|
+
* operationList.do(new NoteDeleteOperation(note));
|
|
113
|
+
*/
|
|
114
|
+
export class NoteDeleteOperation extends NoteRemoveOperation {
|
|
115
|
+
updatesEditor = true
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export class MultiNoteDeleteOperation extends ComplexOperation<NoteDeleteOperation[]> {
|
|
119
|
+
updatesEditor = true
|
|
120
|
+
constructor(notes: Set<Note> | Note[]) {
|
|
121
|
+
if (notes instanceof Set) {
|
|
122
|
+
notes = [...notes];
|
|
123
|
+
}
|
|
124
|
+
super(...notes.map(note => new NoteDeleteOperation(note)))
|
|
125
|
+
if (notes.length === 0) {
|
|
126
|
+
this.ineffective = true
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
export class NoteAddOperation extends Operation {
|
|
135
|
+
noteNode: NoteNode
|
|
136
|
+
note: Note;
|
|
137
|
+
isHold: boolean;
|
|
138
|
+
updatesEditor = true;
|
|
139
|
+
constructor(note: Note, node: NoteNode) {
|
|
140
|
+
super()
|
|
141
|
+
this.note = note;
|
|
142
|
+
this.isHold = note.type === NoteType.hold;
|
|
143
|
+
this.noteNode = node;
|
|
144
|
+
// 一般来说,操作是对于在谱里面的NoteNode,谱外面的不需要操作
|
|
145
|
+
this.comboDelta = note.isFake ? 0 : +1;
|
|
146
|
+
}
|
|
147
|
+
do() {
|
|
148
|
+
const {note, noteNode} = this;
|
|
149
|
+
const needsUpdate = this.isHold && TC.lt(noteNode.endTime, note.endTime);
|
|
150
|
+
if (needsUpdate) {
|
|
151
|
+
const endBeats = TC.toBeats(note.endTime);
|
|
152
|
+
const tailJump = (noteNode.parentSeq as HNList).holdTailJump;
|
|
153
|
+
const updateFrom = tailJump.header
|
|
154
|
+
// tailJump.getNodeAt(endBeats).previous;
|
|
155
|
+
noteNode.add(note)
|
|
156
|
+
tailJump.updateRange(updateFrom, noteNode.next);
|
|
157
|
+
} else {
|
|
158
|
+
noteNode.add(note);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
undo() {
|
|
162
|
+
const {note, noteNode} = this;
|
|
163
|
+
noteNode.remove(note);
|
|
164
|
+
const needsUpdate = this.isHold && TC.lt(noteNode.endTime, note.endTime)
|
|
165
|
+
if (needsUpdate) {
|
|
166
|
+
const endBeats = TC.toBeats(note.endTime);
|
|
167
|
+
const tailJump = (noteNode.parentSeq as HNList).holdTailJump;
|
|
168
|
+
const updateFrom = tailJump.getPreviousOf(noteNode, endBeats);
|
|
169
|
+
tailJump.updateRange(updateFrom, noteNode.next);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export class MultiNoteAddOperation extends ComplexOperation<NoteAddOperation[]> {
|
|
175
|
+
updatesEditor = true;
|
|
176
|
+
constructor(notes: Set<Note> | Note[], judgeLine: JudgeLine) {
|
|
177
|
+
if (notes instanceof Set) {
|
|
178
|
+
notes = [...notes];
|
|
179
|
+
}
|
|
180
|
+
super(...notes.map(note => {
|
|
181
|
+
const node = judgeLine.getNode(note, true)
|
|
182
|
+
return new NoteAddOperation(note, node);
|
|
183
|
+
}));
|
|
184
|
+
// 以上,一般能数清楚加了多少物量
|
|
185
|
+
if (notes.length === 0) {
|
|
186
|
+
this.ineffective = true
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* 修改 Note 的时间位置(不直接修改 startTime,而是将 Note 移动到另一个 NoteNode)
|
|
193
|
+
* 注意:不能直接修改 note.startTime 属性,需要通过 getNodeOf 获取目标时间的 NoteNode
|
|
194
|
+
* @example
|
|
195
|
+
* // 将 note 移动到目标时间位置
|
|
196
|
+
* const targetNode = note.parentNode.parentSeq.getNodeOf(targetTime);
|
|
197
|
+
* operationList.do(new NoteTimeChangeOperation(note, targetNode));
|
|
198
|
+
*/
|
|
199
|
+
export class NoteTimeChangeOperation extends ComplexOperation<[
|
|
200
|
+
NoteRemoveOperation,
|
|
201
|
+
NotePropChangeOperation<"startTime">,
|
|
202
|
+
NoteAddOperation,
|
|
203
|
+
UnionOperation<NotePropChangeOperation<"endTime"> | null>
|
|
204
|
+
]>
|
|
205
|
+
{
|
|
206
|
+
note: Note;
|
|
207
|
+
comboDelta = 0;
|
|
208
|
+
constructor(note: Note, noteNode: NoteNode) {
|
|
209
|
+
super(
|
|
210
|
+
new NoteRemoveOperation(note),
|
|
211
|
+
new NotePropChangeOperation(note, "startTime", noteNode.startTime),
|
|
212
|
+
new NoteAddOperation(note, noteNode),
|
|
213
|
+
new UnionOperation(() => {
|
|
214
|
+
if (note.type !== NoteType.hold) { // 非hold,endTime跟随startTime
|
|
215
|
+
return new NotePropChangeOperation(note, "endTime", noteNode.startTime)
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
);
|
|
219
|
+
this.updatesEditor = true;
|
|
220
|
+
if (note.type === NoteType.hold && !TC.gt(note.endTime, noteNode.startTime)) {
|
|
221
|
+
this.ineffective = true
|
|
222
|
+
}
|
|
223
|
+
this.note = note
|
|
224
|
+
if (note.parentNode === noteNode) {
|
|
225
|
+
this.ineffective = true
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// 真的是巨坑啊
|
|
229
|
+
rewrite(operation: NoteTimeChangeOperation): boolean {
|
|
230
|
+
if (operation.note === this.note) {
|
|
231
|
+
this.subOperations[0] = new NoteRemoveOperation(this.note)
|
|
232
|
+
if (!this.subOperations[0].ineffective) {
|
|
233
|
+
this.subOperations[0].do()
|
|
234
|
+
}
|
|
235
|
+
this.subOperations[1].value = operation.subOperations[1].value
|
|
236
|
+
this.subOperations[1].do()
|
|
237
|
+
this.subOperations[2].noteNode = operation.subOperations[2].noteNode
|
|
238
|
+
this.subOperations[2].do()
|
|
239
|
+
if (this.subOperations[3].operation) {
|
|
240
|
+
this.subOperations[3].operation.value = operation.subOperations[3].operation.value;
|
|
241
|
+
this.subOperations[3].operation.do();
|
|
242
|
+
} else {
|
|
243
|
+
this.subOperations[3] = operation.subOperations[3];
|
|
244
|
+
this.subOperations[3].do();
|
|
245
|
+
}
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
return false
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* 修改 hold 音符的 endTime 属性
|
|
254
|
+
* 直接修改 hold 音符的 endTime(不同于 NoteTimeChangeOperation)
|
|
255
|
+
* @example
|
|
256
|
+
* // 修改 hold 音符的结束时间
|
|
257
|
+
* operationList.do(new HoldEndTimeChangeOperation(note, newEndTime));
|
|
258
|
+
*/
|
|
259
|
+
export class HoldEndTimeChangeOperation extends NotePropChangeOperation<"endTime"> {
|
|
260
|
+
constructor(note: Note, value: TimeT) {
|
|
261
|
+
super(note, "endTime", value)
|
|
262
|
+
if (!TC.gt(value, note.startTime)) {
|
|
263
|
+
this.ineffective = true
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
do() {
|
|
267
|
+
super.do()
|
|
268
|
+
const node = this.note.parentNode;
|
|
269
|
+
node.sort(this.note);
|
|
270
|
+
const tailJump = (node.parentSeq as HNList).holdTailJump;
|
|
271
|
+
tailJump.updateRange(tailJump.header, tailJump.tailer);
|
|
272
|
+
}
|
|
273
|
+
undo() {
|
|
274
|
+
super.undo()
|
|
275
|
+
const node = this.note.parentNode;
|
|
276
|
+
node.sort(this.note);
|
|
277
|
+
const tailJump = (node.parentSeq as HNList).holdTailJump;
|
|
278
|
+
tailJump.updateRange(tailJump.header, tailJump.tailer);
|
|
279
|
+
}
|
|
280
|
+
rewrite(operation: HoldEndTimeChangeOperation): boolean { // 看懂了,不重写的话会出问题
|
|
281
|
+
if (operation.note === this.note && this.field === operation.field) {
|
|
282
|
+
if (operation.value === this.value) {
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
285
|
+
this.value = operation.value;
|
|
286
|
+
this.note[this.field] = operation.value;
|
|
287
|
+
const tailJump = (this.note.parentNode.parentSeq as HNList).holdTailJump;
|
|
288
|
+
tailJump.updateRange(tailJump.header, tailJump.tailer);
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* 修改 Note 的 speed 属性
|
|
299
|
+
* 需要传入 JudgeLine 参数,因为 speed 变化会影响 note 在何 NNList 中
|
|
300
|
+
* @example
|
|
301
|
+
* // 修改 note 的 speed 值
|
|
302
|
+
* operationList.do(new NoteSpeedChangeOperation(note, 2.0, judgeLine));
|
|
303
|
+
*/
|
|
304
|
+
export class NoteSpeedChangeOperation
|
|
305
|
+
extends ComplexOperation<[NotePropChangeOperation<"speed">, NoteRemoveOperation, NoteAddOperation]> {
|
|
306
|
+
updatesEditor = true
|
|
307
|
+
constructor(public note: Note, value: number, line: JudgeLine) {
|
|
308
|
+
const valueChange = new NotePropChangeOperation(note, "speed", value);
|
|
309
|
+
const tree = line.getNNList(value, note.yOffset, note.type === NoteType.hold, true)
|
|
310
|
+
const node = tree.getNodeOf(note.startTime);
|
|
311
|
+
const removal = new NoteRemoveOperation(note);
|
|
312
|
+
const insert = new NoteAddOperation(note, node)
|
|
313
|
+
super(valueChange, removal, insert);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export class NoteYOffsetChangeOperation
|
|
318
|
+
extends ComplexOperation<[NotePropChangeOperation<"yOffset">, NoteRemoveOperation, NoteAddOperation]> {
|
|
319
|
+
updatesEditor = true
|
|
320
|
+
constructor(public note: Note, value: number, line: JudgeLine) {
|
|
321
|
+
const valueChange = new NotePropChangeOperation(note, "yOffset", value);
|
|
322
|
+
const tree = line.getNNList(note.speed, value, note.type === NoteType.hold, true)
|
|
323
|
+
const node = tree.getNodeOf(note.startTime);
|
|
324
|
+
const removal = new NoteRemoveOperation(note);
|
|
325
|
+
const insert = new NoteAddOperation(note, node)
|
|
326
|
+
super(valueChange, removal, insert);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* 修改 Note 的 type 属性(音符类型)
|
|
333
|
+
* 在 tap/drag/flick/hold 之间切换,切换到/从 hold 时需要重新定位 NoteNode
|
|
334
|
+
* @example
|
|
335
|
+
* // 将 note 改为 hold 类型
|
|
336
|
+
* operationList.do(new NoteTypeChangeOperation(note, NoteType.hold));
|
|
337
|
+
*/
|
|
338
|
+
export class NoteTypeChangeOperation
|
|
339
|
+
extends ComplexOperation<[NotePropChangeOperation<"type">, NoteRemoveOperation, NoteAddOperation] | [NotePropChangeOperation<"type">]> {
|
|
340
|
+
note: Note;
|
|
341
|
+
value: number;
|
|
342
|
+
constructor(note: Note, value: number) {
|
|
343
|
+
const isHold = note.type === NoteType.hold
|
|
344
|
+
const valueChange = new NotePropChangeOperation(note, "type", value);
|
|
345
|
+
if (isHold !== (value === NoteType.hold)) {
|
|
346
|
+
const tree = note.parentNode.parentSeq.parentLine.getNNList(note.speed, note.yOffset, !isHold, true)
|
|
347
|
+
const node = tree.getNodeOf(note.startTime);
|
|
348
|
+
const removal = new NoteRemoveOperation(note);
|
|
349
|
+
const insert = new NoteAddOperation(note, node);
|
|
350
|
+
super(valueChange, removal, insert);
|
|
351
|
+
} else {
|
|
352
|
+
super(valueChange);
|
|
353
|
+
}
|
|
354
|
+
this.note = note;
|
|
355
|
+
this.value = value;
|
|
356
|
+
this.updatesEditor = true;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
class NoteTreeChangeOperation extends NoteAddOperation {
|
|
361
|
+
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
// 按照规矩,音符节点的时间不可变,所以不会对音符节点动手。
|
|
366
|
+
// 依旧不用组合的NoteTimeChangeOperation的方式,因为那需要多次更新跳数组。
|
|
367
|
+
/**
|
|
368
|
+
* @author Zes Minkey Young
|
|
369
|
+
*/
|
|
370
|
+
// @ts-expect-error 我需要明确禁用掉lazy静态方法,这不会破坏类型安全。
|
|
371
|
+
export class MultiNoteOffsetOperation extends Operation {
|
|
372
|
+
constructor(public nnList: NNList, public notes: readonly Note[], public offset: TimeT) {
|
|
373
|
+
super();
|
|
374
|
+
}
|
|
375
|
+
do() {
|
|
376
|
+
const offset = this.offset;
|
|
377
|
+
const notes = this.notes;
|
|
378
|
+
const len = notes.length;
|
|
379
|
+
const nnList = this.nnList;
|
|
380
|
+
for (let i = 0; i < len; i++) {
|
|
381
|
+
const note = notes[i];
|
|
382
|
+
const startTime = TC.vadd(note.startTime, offset);
|
|
383
|
+
note.startTime = startTime;
|
|
384
|
+
note.endTime = TC.vadd(note.endTime, offset);
|
|
385
|
+
note.parentNode.remove(note);
|
|
386
|
+
nnList.getNodeOf(startTime).add(note);
|
|
387
|
+
}
|
|
388
|
+
nnList.jump.updateRange(nnList.head, nnList.tail);
|
|
389
|
+
if (nnList instanceof HNList) {
|
|
390
|
+
nnList.holdTailJump.updateRange(nnList.head, nnList.tail);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
undo() {
|
|
394
|
+
const offset = this.offset;
|
|
395
|
+
const notes = this.notes;
|
|
396
|
+
const len = notes.length;
|
|
397
|
+
const nnList = this.nnList;
|
|
398
|
+
for (let i = 0; i < len; i++) {
|
|
399
|
+
const note = notes[i];
|
|
400
|
+
const startTime = TC.vsub(note.startTime, offset);
|
|
401
|
+
note.startTime = startTime;
|
|
402
|
+
note.endTime = TC.vsub(note.endTime, offset);
|
|
403
|
+
note.parentNode.remove(note);
|
|
404
|
+
nnList.getNodeOf(startTime).add(note);
|
|
405
|
+
}
|
|
406
|
+
nnList.jump.updateRange(nnList.head, nnList.tail);
|
|
407
|
+
if (nnList instanceof HNList) {
|
|
408
|
+
nnList.holdTailJump.updateRange(nnList.head, nnList.tail);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
// 禁用,因为无效
|
|
412
|
+
private static lazy() {}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
type TimeRange = [TimeT, TimeT]
|
|
417
|
+
|
|
418
|
+
export class NNListTimeRangeDeleteOperation extends ComplexOperation<[MultiNoteDeleteOperation, MultiNoteOffsetOperation]> {
|
|
419
|
+
constructor(public nnList: NNList, public timeRange: TimeRange, public updatesJump: boolean = true) {
|
|
420
|
+
const delNodes = nnList.getNodesFromOneAndRangeRight(nnList.getNodeOf(timeRange[0]), timeRange[1]);
|
|
421
|
+
const delNotes = [];
|
|
422
|
+
const dlen = delNodes.length;
|
|
423
|
+
for (let i = 0; i < dlen; i++) {
|
|
424
|
+
delNotes.push(...delNodes[i].notes);
|
|
425
|
+
}
|
|
426
|
+
const offsetNodes = nnList.getNodesAfterOne(delNodes[dlen - 1]);
|
|
427
|
+
const offsetNotes = [];
|
|
428
|
+
const olen = offsetNodes.length;
|
|
429
|
+
for (let i = 0; i < olen; i++) {
|
|
430
|
+
offsetNotes.push(...offsetNodes[i].notes);
|
|
431
|
+
}
|
|
432
|
+
super(new MultiNoteDeleteOperation(delNotes), new MultiNoteOffsetOperation(nnList, offsetNotes, TC.vsub(timeRange[0], timeRange[1])));
|
|
433
|
+
}
|
|
434
|
+
do() {
|
|
435
|
+
super.do();
|
|
436
|
+
this.nnList.clearEmptyNodes(this.updatesJump)
|
|
437
|
+
}
|
|
438
|
+
undo() {
|
|
439
|
+
super.undo();
|
|
440
|
+
this.nnList.clearEmptyNodes(this.updatesJump)
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export class NNListAddBlankOperation extends MultiNoteOffsetOperation {
|
|
445
|
+
updatesEditor = true;
|
|
446
|
+
constructor(nnList: NNList, pos: TimeT, length: TimeT) {
|
|
447
|
+
const nns = nnList.getNodesAfterOne(nnList.getNodeOf(pos));
|
|
448
|
+
const notes = [];
|
|
449
|
+
const len = nns.length;
|
|
450
|
+
for (let i = 0; i < len; i++) {
|
|
451
|
+
notes.push(...nns[i].notes);
|
|
452
|
+
}
|
|
453
|
+
super(nnList, notes, length);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kipphi",
|
|
3
3
|
"description": "Parse your Phigros Chart(.rpe.json or .kpa.json) into an editor-friendly format.",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"author": "Team Zincs (https://github.com/TeamZincs)",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -16,5 +16,11 @@
|
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
18
|
"typescript": "^5"
|
|
19
|
+
},
|
|
20
|
+
"exports": {
|
|
21
|
+
"./operation": null,
|
|
22
|
+
".": {
|
|
23
|
+
"import": "./index.ts"
|
|
24
|
+
}
|
|
19
25
|
}
|
|
20
26
|
}
|