kipphi 2.1.2 → 2.1.3-beta.2
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/bpm.ts +26 -6
- package/chart.ts +42 -2
- package/chartTypes.ts +26 -5
- package/easing.ts +31 -2
- package/env.ts +6 -0
- package/evaluator.ts +39 -14
- package/event.ts +50 -9
- package/index.d.ts +77 -11
- package/index.js +240 -79
- package/judgeline.ts +100 -43
- package/note.ts +19 -21
- package/operation/easy.ts +136 -0
- package/operation/event.ts +7 -0
- package/package.json +1 -1
- package/rpeChartCompiler.ts +39 -10
- package/tsconfig.json +2 -2
- package/util.ts +4 -2
- package/version.ts +1 -1
package/judgeline.ts
CHANGED
|
@@ -186,17 +186,18 @@ export class JudgeLine {
|
|
|
186
186
|
line.speedSequence = speedSequences[0]
|
|
187
187
|
chart.registerEventNodeSequence(EventType.speed, `#${id}.speed`, speedSequences[0]);
|
|
188
188
|
}
|
|
189
|
+
|
|
190
|
+
if (data.extended?.scaleXEvents) {
|
|
191
|
+
line.extendedLayer.scaleX = createExtendedSequence(EventType.scaleX, data.extended.scaleXEvents);
|
|
192
|
+
} else {
|
|
193
|
+
line.extendedLayer.scaleX = chart.createEventNodeSequence(EventType.scaleX, `#${id}.ex.scaleX`);
|
|
194
|
+
}
|
|
195
|
+
if (data.extended?.scaleYEvents) {
|
|
196
|
+
line.extendedLayer.scaleY = createExtendedSequence(EventType.scaleY, data.extended.scaleYEvents);
|
|
197
|
+
} else {
|
|
198
|
+
line.extendedLayer.scaleY = chart.createEventNodeSequence(EventType.scaleY, `#${id}.ex.scaleY`);
|
|
199
|
+
}
|
|
189
200
|
if (data.extended) {
|
|
190
|
-
if (data.extended.scaleXEvents) {
|
|
191
|
-
line.extendedLayer.scaleX = createExtendedSequence(EventType.scaleX, data.extended.scaleXEvents);
|
|
192
|
-
} else {
|
|
193
|
-
line.extendedLayer.scaleX = chart.createEventNodeSequence(EventType.scaleX, `#${id}.ex.scaleX`);
|
|
194
|
-
}
|
|
195
|
-
if (data.extended.scaleYEvents) {
|
|
196
|
-
line.extendedLayer.scaleY = createExtendedSequence(EventType.scaleY, data.extended.scaleYEvents);
|
|
197
|
-
} else {
|
|
198
|
-
line.extendedLayer.scaleY = chart.createEventNodeSequence(EventType.scaleY, `#${id}.ex.scaleY`);
|
|
199
|
-
}
|
|
200
201
|
if (data.extended.textEvents) {
|
|
201
202
|
line.extendedLayer.text = createExtendedSequence(EventType.text, data.extended.textEvents);
|
|
202
203
|
}
|
|
@@ -572,8 +573,9 @@ export class JudgeLine {
|
|
|
572
573
|
|
|
573
574
|
|
|
574
575
|
const computeTime = (speed: number, currentPos: number, fore: number) =>
|
|
575
|
-
timeCalculator.secondsToBeats(currentPos / (speed * 120) + timeCalculator.toSeconds(fore));
|
|
576
|
+
speed * currentPos <= 0 ? fore : timeCalculator.secondsToBeats(currentPos / (speed * 120) + timeCalculator.toSeconds(fore));
|
|
576
577
|
|
|
578
|
+
|
|
577
579
|
// 遍历所有事件节点直到结尾
|
|
578
580
|
while (true) {
|
|
579
581
|
const thisTime = TC.toBeats(startNode.time);
|
|
@@ -584,24 +586,62 @@ export class JudgeLine {
|
|
|
584
586
|
const thisPosY = startNode.floorPosition - currentJudgeLineFloorPos;
|
|
585
587
|
const thisSpeed = startNode.value;
|
|
586
588
|
|
|
587
|
-
const inf = thisSpeed > 0 ? Infinity : (thisSpeed < 0 ? -Infinity : thisPosY);
|
|
588
589
|
|
|
589
590
|
if (range[0] === undefined) {
|
|
590
|
-
|
|
591
|
+
/*
|
|
592
|
+
有交集
|
|
593
|
+
******* +INF ******
|
|
594
|
+
|
|
595
|
+
------ endY -------
|
|
596
|
+
|
|
|
597
|
+
|
|
|
598
|
+
|
|
|
599
|
+
----- thisPosY ----
|
|
600
|
+
|
|
601
|
+
*/
|
|
602
|
+
if (thisSpeed > 0 && endY > thisPosY) {
|
|
591
603
|
range[0] = computeTime(
|
|
592
604
|
thisSpeed,
|
|
593
|
-
|
|
605
|
+
startY - thisPosY,
|
|
594
606
|
thisTime)
|
|
595
|
-
|
|
596
|
-
|
|
607
|
+
/*
|
|
608
|
+
有交集
|
|
609
|
+
----- thisPosY ----
|
|
610
|
+
|
|
|
611
|
+
|
|
|
612
|
+
|
|
|
613
|
+
------ startY -------
|
|
614
|
+
|
|
615
|
+
******* -INF ******
|
|
616
|
+
*/
|
|
617
|
+
} else if (thisSpeed < 0 && startY < thisPosY) {
|
|
618
|
+
range[0] = computeTime(
|
|
619
|
+
thisSpeed,
|
|
620
|
+
endY - thisPosY,
|
|
621
|
+
thisTime)
|
|
622
|
+
} else if (thisSpeed === 0 && startY <= thisPosY && thisPosY <= endY) {
|
|
623
|
+
range[0] = thisTime;
|
|
597
624
|
}
|
|
598
625
|
}
|
|
599
626
|
|
|
600
627
|
if (range[0] !== undefined) {
|
|
601
|
-
|
|
628
|
+
/*
|
|
629
|
+
********** +INF **********
|
|
630
|
+
|
|
631
|
+
---------- endY ----------
|
|
632
|
+
|
|
633
|
+
-------- thisPosY --------
|
|
634
|
+
*/
|
|
635
|
+
if (thisSpeed > 0 && endY > thisPosY) {
|
|
636
|
+
range[1] = computeTime(
|
|
637
|
+
thisSpeed,
|
|
638
|
+
endY - thisPosY,
|
|
639
|
+
thisTime);
|
|
640
|
+
result.push(range);
|
|
641
|
+
} else if (thisSpeed < 0 && startY < thisPosY) {
|
|
602
642
|
range[1] = computeTime(
|
|
603
643
|
thisSpeed,
|
|
604
|
-
|
|
644
|
+
startY - thisPosY,
|
|
605
645
|
thisTime)
|
|
606
646
|
result.push(range)
|
|
607
647
|
} else if (thisSpeed === 0) {
|
|
@@ -619,7 +659,7 @@ export class JudgeLine {
|
|
|
619
659
|
const nextPosY = nextStart.floorPosition - currentJudgeLineFloorPos;
|
|
620
660
|
|
|
621
661
|
let thisSpeed = startNode.value;
|
|
622
|
-
let nextSpeed =
|
|
662
|
+
let nextSpeed = endNode.value;
|
|
623
663
|
|
|
624
664
|
if (Math.abs(thisSpeed) < 1e-8) {
|
|
625
665
|
thisSpeed = 0;
|
|
@@ -638,21 +678,14 @@ export class JudgeLine {
|
|
|
638
678
|
|
|
639
679
|
// 处理第一段(开始到零点)
|
|
640
680
|
if (range[0] === undefined) {
|
|
641
|
-
if (
|
|
642
|
-
range[0] = thisSpeed !== zeroSpeed ? thisTime : computeTime(
|
|
643
|
-
thisSpeed,
|
|
644
|
-
(thisPosY < zeroPosY ? startY : endY) - thisPosY, thisTime
|
|
645
|
-
);
|
|
646
|
-
} else if (startY <= thisPosY && thisPosY <= endY) {
|
|
681
|
+
if (thisSpeed > 0 && endY >= thisPosY || thisSpeed < 0 && startY <= thisPosY) {
|
|
647
682
|
range[0] = thisTime;
|
|
648
683
|
}
|
|
649
684
|
}
|
|
650
685
|
|
|
651
686
|
if (range[0] !== undefined) {
|
|
652
|
-
if (
|
|
653
|
-
range[1] =
|
|
654
|
-
thisSpeed,
|
|
655
|
-
(thisPosY > zeroPosY ? startY : endY) - thisPosY, thisTime)
|
|
687
|
+
if (thisSpeed > 0 && endY <= zeroPosY || thisSpeed < 0 && startY >= zeroPosY) {
|
|
688
|
+
range[1] = zeroTime;
|
|
656
689
|
result.push(range);
|
|
657
690
|
if (lineMonotonicity !== Monotonicity.swinging) {
|
|
658
691
|
// 单调的FloorPosition函数只能产生一个符合条件的区间,可以提前返回达到优化目的
|
|
@@ -664,20 +697,14 @@ export class JudgeLine {
|
|
|
664
697
|
|
|
665
698
|
// 处理第二段(零点到结束)
|
|
666
699
|
if (range[0] === undefined) {
|
|
667
|
-
if (
|
|
668
|
-
range[0] = zeroSpeed !== nextSpeed ? zeroTime : computeTime(
|
|
669
|
-
nextSpeed,
|
|
670
|
-
(zeroPosY < nextPosY ? startY : endY) - zeroPosY, zeroTime)
|
|
671
|
-
} else if (startY <= zeroPosY && zeroPosY <= endY) {
|
|
700
|
+
if (nextSpeed > 0 && endY >= zeroPosY || nextSpeed < 0 && startY <= zeroPosY) {
|
|
672
701
|
range[0] = zeroTime;
|
|
673
702
|
}
|
|
674
703
|
}
|
|
675
704
|
|
|
676
705
|
if (range[0] !== undefined) {
|
|
677
|
-
if (
|
|
678
|
-
range[1] =
|
|
679
|
-
nextSpeed,
|
|
680
|
-
(zeroPosY > nextPosY ? startY : endY) - zeroPosY, zeroTime)
|
|
706
|
+
if (nextSpeed > 0 && endY <= nextPosY || nextSpeed < 0 && startY >= nextPosY) {
|
|
707
|
+
range[1] = nextTime
|
|
681
708
|
result.push(range)
|
|
682
709
|
if (lineMonotonicity !== Monotonicity.swinging) {
|
|
683
710
|
// 单调的FloorPosition函数只能产生一个符合条件的区间,可以提前返回达到优化目的
|
|
@@ -689,20 +716,37 @@ export class JudgeLine {
|
|
|
689
716
|
} else {
|
|
690
717
|
// 正常情况处理
|
|
691
718
|
if (range[0] === undefined) {
|
|
692
|
-
if (
|
|
719
|
+
if (thisSpeed > 0 && endY >= thisPosY && startY < nextPosY) {
|
|
720
|
+
range[0] = thisSpeed !== nextSpeed ? thisTime : computeTime(
|
|
721
|
+
thisSpeed,
|
|
722
|
+
startY - thisPosY, thisTime)
|
|
723
|
+
} else if (thisSpeed < 0 && startY <= thisPosY && endY > nextPosY) {
|
|
693
724
|
range[0] = thisSpeed !== nextSpeed ? thisTime : computeTime(
|
|
694
725
|
thisSpeed,
|
|
695
|
-
|
|
696
|
-
} else if (startY <= thisPosY && thisPosY <= endY) {
|
|
726
|
+
endY - thisPosY, thisTime)
|
|
727
|
+
} else if (thisSpeed === 0 && startY <= thisPosY && thisPosY <= endY) {
|
|
697
728
|
range[0] = thisTime;
|
|
698
729
|
}
|
|
730
|
+
// else if (startY <= thisPosY && thisPosY <= endY) {
|
|
731
|
+
// range[0] = thisTime;
|
|
732
|
+
// }
|
|
699
733
|
}
|
|
700
734
|
|
|
701
735
|
if (range[0] !== undefined) {
|
|
702
|
-
if (
|
|
736
|
+
if (thisSpeed > 0 && endY <= nextPosY) {
|
|
737
|
+
range[1] = thisSpeed !== nextSpeed ? nextTime : computeTime(
|
|
738
|
+
thisSpeed,
|
|
739
|
+
endY - thisPosY, thisTime)
|
|
740
|
+
result.push(range);
|
|
741
|
+
if (lineMonotonicity !== Monotonicity.swinging) {
|
|
742
|
+
// 单调的FloorPosition函数只能产生一个符合条件的区间,可以提前返回达到优化目的
|
|
743
|
+
return result;
|
|
744
|
+
}
|
|
745
|
+
range = [undefined, undefined];
|
|
746
|
+
} else if (thisSpeed < 0 && startY >= thisPosY) {
|
|
703
747
|
range[1] = thisSpeed !== nextSpeed ? nextTime : computeTime(
|
|
704
748
|
thisSpeed,
|
|
705
|
-
|
|
749
|
+
startY - thisPosY, thisTime)
|
|
706
750
|
result.push(range);
|
|
707
751
|
if (lineMonotonicity !== Monotonicity.swinging) {
|
|
708
752
|
// 单调的FloorPosition函数只能产生一个符合条件的区间,可以提前返回达到优化目的
|
|
@@ -749,6 +793,19 @@ export class JudgeLine {
|
|
|
749
793
|
}
|
|
750
794
|
return current
|
|
751
795
|
}
|
|
796
|
+
getStackedValueBySeconds(type: keyof EventLayer, beats: number, seconds: number, timeCalculator: TimeCalculator, usePrev: boolean = false) {
|
|
797
|
+
|
|
798
|
+
const length = this.eventLayers.length;
|
|
799
|
+
let current = 0;
|
|
800
|
+
for (let index = 0; index < length; index++) {
|
|
801
|
+
const layer = this.eventLayers[index];
|
|
802
|
+
if (!layer || !layer[type]) {
|
|
803
|
+
break;
|
|
804
|
+
}
|
|
805
|
+
current += layer[type].getValueAtBySecs(beats, seconds, timeCalculator, usePrev);
|
|
806
|
+
}
|
|
807
|
+
return current
|
|
808
|
+
}
|
|
752
809
|
/**
|
|
753
810
|
* 获取指定时间点的FloorPosition。
|
|
754
811
|
*
|
package/note.ts
CHANGED
|
@@ -20,20 +20,20 @@ export type HEX = number;
|
|
|
20
20
|
|
|
21
21
|
export const notePropTypes = {
|
|
22
22
|
above: "boolean",
|
|
23
|
-
alpha: "
|
|
23
|
+
alpha: "int[0,255]",
|
|
24
24
|
endTime: ["number", "number", "number"],
|
|
25
25
|
isFake: "boolean",
|
|
26
26
|
positionX: "number",
|
|
27
|
-
size: "number",
|
|
27
|
+
size: "number(0,+)",
|
|
28
28
|
speed: "number",
|
|
29
29
|
startTime: ["number", "number", "number"],
|
|
30
|
-
type: "
|
|
31
|
-
visibleTime: "number",
|
|
32
|
-
visibleBeats: "number",
|
|
30
|
+
type: "int[1,4]",
|
|
31
|
+
visibleTime: "number(0,+)",
|
|
32
|
+
visibleBeats: "number(0,+)",
|
|
33
33
|
yOffset: "number",
|
|
34
|
-
tint: ["
|
|
35
|
-
tintHitEffects: ["
|
|
36
|
-
judgeSize: "number"
|
|
34
|
+
tint: ["int[0,255]", "int[0,255]", "int[0,255]"],
|
|
35
|
+
tintHitEffects: ["int[0,255]", "int[0,255]", "int[0,255]"],
|
|
36
|
+
judgeSize: "number(0,+)"
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
@@ -102,15 +102,12 @@ export class Note {
|
|
|
102
102
|
// 当然也有可能是KPA数据但是就是没有给
|
|
103
103
|
this.visibleBeats = data.visibleBeats;
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
const color = data.tint ?? (data as NoteDataRPE).color;
|
|
106
|
+
|
|
107
|
+
this.tint = color ? rgb2hex(color) : undefined;
|
|
106
108
|
this.tintHitEffects = data.tintHitEffects ? rgb2hex(data.tintHitEffects) : undefined;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
this.previous = null;
|
|
110
|
-
this.next = null;
|
|
111
|
-
this.previousSibling = null;
|
|
112
|
-
this.nextSibling = null;
|
|
113
|
-
*/
|
|
109
|
+
// @ts-expect-error KPA用judgeSize,RPE用judgeArea,PZP用judgeSize
|
|
110
|
+
this.judgeSize = data.judgeSize ?? data.judgeArea ?? this.size;
|
|
114
111
|
}
|
|
115
112
|
static fromKPAJSON(data: NoteDataKPA, timeCalculator: TimeCalculator) {
|
|
116
113
|
const note = new Note(data);
|
|
@@ -171,8 +168,9 @@ export class Note {
|
|
|
171
168
|
visibleTime: visibleTime,
|
|
172
169
|
yOffset: this.yOffset / this.speed,
|
|
173
170
|
speed: this.speed,
|
|
174
|
-
tint: this.tint !== undefined ? hex2rgb(this.tint) : undefined,
|
|
175
|
-
tintHitEffects: this.
|
|
171
|
+
tint: this.tint !== undefined && this.tint !== 0xffffff ? hex2rgb(this.tint) : undefined,
|
|
172
|
+
tintHitEffects: this.tintHitEffects !== undefined && this.tintHitEffects !== 0xffffff ? hex2rgb(this.tintHitEffects) : undefined,
|
|
173
|
+
judgeArea: this.judgeSize
|
|
176
174
|
}
|
|
177
175
|
}
|
|
178
176
|
dumpKPA(): NoteDataKPA {
|
|
@@ -191,8 +189,8 @@ export class Note {
|
|
|
191
189
|
/** 但是有历史包袱,所以加字段 */
|
|
192
190
|
absoluteYOffset: this.yOffset,
|
|
193
191
|
speed: this.speed,
|
|
194
|
-
tint: this.tint !== undefined ? hex2rgb(this.tint) : undefined,
|
|
195
|
-
tintHitEffects: this.
|
|
192
|
+
tint: this.tint !== undefined && this.tint !== 0xffffff ? hex2rgb(this.tint) : undefined,
|
|
193
|
+
tintHitEffects: this.tintHitEffects !== undefined && this.tintHitEffects !== 0xffffff ? hex2rgb(this.tintHitEffects) : undefined,
|
|
196
194
|
judgeSize: this.judgeSize && this.judgeSize !== 1.0 ? this.judgeSize : undefined,
|
|
197
195
|
}
|
|
198
196
|
}
|
|
@@ -375,7 +373,7 @@ export class NNList {
|
|
|
375
373
|
}
|
|
376
374
|
/** 此方法永远用于最新KPAJSON */
|
|
377
375
|
static fromKPAJSON<T extends boolean>(isHold: T, effectiveBeats: number, data: NNListDataKPA, nnnList: NNNList, timeCalculator: TimeCalculator): T extends true ? HNList : NNList {
|
|
378
|
-
const list: T extends true ? HNList : NNList = isHold ? new HNList(data.speed, data.medianYOffset, effectiveBeats) : new NNList(data.speed, data.medianYOffset, effectiveBeats)
|
|
376
|
+
const list: T extends true ? HNList : NNList = isHold ? new HNList(data.speed, data.medianYOffset, effectiveBeats) : new NNList(data.speed, data.medianYOffset, effectiveBeats) as any;
|
|
379
377
|
const nnlength = data.noteNodes.length
|
|
380
378
|
let cur: NNOrHead = list.head;
|
|
381
379
|
for (let i = 0; i < nnlength; i++) {
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { NoteType, RGB, TimeT } from "../chartTypes";
|
|
2
|
+
import { EventNode } from "../event";
|
|
3
|
+
import { HEX, Note, NoteNode } from "../note";
|
|
4
|
+
import TC from "../time";
|
|
5
|
+
import { numberToRatio } from "../util";
|
|
6
|
+
import { type Operation } from "./basic";
|
|
7
|
+
import { HoldEndTimeChangeOperation, NotePropChangeOperation, NotePropName, NoteTimeChangeOperation, NoteTypeChangeOperation } from "./note"
|
|
8
|
+
|
|
9
|
+
type IntoOperable = EventNode | NoteNode | Note;
|
|
10
|
+
|
|
11
|
+
type Time = TimeT | number | string;
|
|
12
|
+
|
|
13
|
+
type ToOperable = ((o: EventNode) => EventNode) & { buffer: Operation[] };
|
|
14
|
+
|
|
15
|
+
const userTimeToTuple = (time: Time) => {
|
|
16
|
+
if (typeof time === "string") {
|
|
17
|
+
const match = time.match(/^(\d+):(\d+)\/(\d+)$/);
|
|
18
|
+
if (!match) {
|
|
19
|
+
throw new Error(`Invalid time format: ${time}`);
|
|
20
|
+
}
|
|
21
|
+
return userTimeToTuple(match.map(s => parseInt(s)) as TimeT);
|
|
22
|
+
} else if (typeof time === "number") {
|
|
23
|
+
const integer = Math.floor(time);
|
|
24
|
+
return TC.validateIp([integer, ...numberToRatio(time - integer)]);
|
|
25
|
+
} else {
|
|
26
|
+
time = [...time]; // 防止从别的什么地方找来一个数据
|
|
27
|
+
TC.validateIp(time); // 原地规范化,如果失败就会抛错误,这里不用捕获
|
|
28
|
+
return time;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class Operable {
|
|
33
|
+
constructor(public buffer: Operation[]) {}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class OperableNote extends Operable {
|
|
38
|
+
// @ts-expect-error 后面会赋值
|
|
39
|
+
private _fields: {
|
|
40
|
+
[x in NotePropName]: Note[x]
|
|
41
|
+
} = {};
|
|
42
|
+
constructor(public target: Note, buffer: Operation[]) {
|
|
43
|
+
super(buffer);
|
|
44
|
+
if (target.parentNode === null) {
|
|
45
|
+
throw new Error("Note has no parent node")
|
|
46
|
+
}
|
|
47
|
+
this._fields.startTime = target.startTime;
|
|
48
|
+
this._fields.endTime = target.endTime;
|
|
49
|
+
this._fields.type = target.type;
|
|
50
|
+
}
|
|
51
|
+
get startTime() {
|
|
52
|
+
return this._fields.startTime;
|
|
53
|
+
}
|
|
54
|
+
set startTime(userTime: Time) {
|
|
55
|
+
const timeT = userTimeToTuple(userTime);
|
|
56
|
+
const beats = TC.toBeats(timeT);
|
|
57
|
+
if (beats < 0) {
|
|
58
|
+
throw new Error("")
|
|
59
|
+
}
|
|
60
|
+
const nnList = this.target.parentNode.parentSeq
|
|
61
|
+
if (beats > nnList.effectiveBeats) {
|
|
62
|
+
throw new Error("")
|
|
63
|
+
}
|
|
64
|
+
this._fields.startTime = timeT;
|
|
65
|
+
const node = nnList.getNodeOf(timeT);
|
|
66
|
+
this.buffer.push(NoteTimeChangeOperation.lazy(this.target, node));
|
|
67
|
+
}
|
|
68
|
+
get endTime() { return this._fields.endTime; }
|
|
69
|
+
set endTime(userTime: Time) {
|
|
70
|
+
if (this._fields.type !== NoteType.hold) {
|
|
71
|
+
throw new Error("Note is not a hold note");
|
|
72
|
+
}
|
|
73
|
+
const timeT = userTimeToTuple(userTime);
|
|
74
|
+
if (!TC.gt(timeT, this._fields.startTime)) {
|
|
75
|
+
throw new Error("");
|
|
76
|
+
}
|
|
77
|
+
this._fields.endTime = timeT;
|
|
78
|
+
this.buffer.push(HoldEndTimeChangeOperation.lazy(this.target, timeT));
|
|
79
|
+
}
|
|
80
|
+
get type() { return this._fields.type; }
|
|
81
|
+
set type(type: NoteType) {
|
|
82
|
+
if (this._fields.type === type) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
this._fields.type = type;
|
|
86
|
+
this.buffer.push(NoteTypeChangeOperation.lazy(this.target, type));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface OperableNote {
|
|
91
|
+
get above(): boolean;
|
|
92
|
+
set above(above: boolean);
|
|
93
|
+
get alpha(): number;
|
|
94
|
+
set alpha(alpha: number);
|
|
95
|
+
get positionX(): number;
|
|
96
|
+
set positionX(positionX: number);
|
|
97
|
+
get judgeSize(): number;
|
|
98
|
+
set judgeSize(judgeSize: number);
|
|
99
|
+
get isFake(): boolean;
|
|
100
|
+
set isFake(isFake: boolean);
|
|
101
|
+
get size(): number;
|
|
102
|
+
set size(size: number);
|
|
103
|
+
get tint(): HEX;
|
|
104
|
+
set tint(tint: string | RGB | HEX);
|
|
105
|
+
get tintHitEffects(): HEX;
|
|
106
|
+
set tintHitEffects(tintHitEffects: string | RGB | HEX);
|
|
107
|
+
get visibleBeats(): number;
|
|
108
|
+
set visibleBeats(visibleBeats: number);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
for (const propName of ["above", "alpha", "positionX", "judgeSize", "isFake", "size", "tint", "tintHitEffects", "visibleBeats"] satisfies NotePropName[]) {
|
|
112
|
+
Object.defineProperty(OperableNote.prototype, propName, {
|
|
113
|
+
get() { return this._fields[propName] ?? this.target[propName]},
|
|
114
|
+
set(value) {
|
|
115
|
+
if (this._fields[propName] === value) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
this._fields[propName] = value;
|
|
119
|
+
this.buffer.push(NotePropChangeOperation.lazy(this.target, propName, value));
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function useToOperable(): ToOperable {
|
|
125
|
+
const toOperable = (o: EventNode) => {
|
|
126
|
+
return
|
|
127
|
+
};
|
|
128
|
+
(toOperable as ToOperable).buffer = [];
|
|
129
|
+
return toOperable as ToOperable;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function operate(fn: (o: ToOperable) => void) {
|
|
133
|
+
const o = useToOperable();
|
|
134
|
+
fn(o);
|
|
135
|
+
return o.buffer;
|
|
136
|
+
}
|
package/operation/event.ts
CHANGED
|
@@ -224,6 +224,13 @@ export class EventNodeEvaluatorChangeOperation <VT extends EventValueESType> ext
|
|
|
224
224
|
constructor(public node: EventStartNode<VT>, public value: Evaluator<VT>) {
|
|
225
225
|
super();
|
|
226
226
|
this.originalValue = this.node.evaluator
|
|
227
|
+
const seq = node.parentSeq;
|
|
228
|
+
if (seq.type === EventType.easing && value instanceof EasedEvaluator && value.easing instanceof TemplateEasing) {
|
|
229
|
+
const circular = TemplateEasing.checkCircularReference(seq as EventNodeSequence<number>, value.easing);
|
|
230
|
+
if (circular) {
|
|
231
|
+
throw err.TEMPLATE_EASING_CIRCULAR_REFERENCE(value.easing.name);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
227
234
|
}
|
|
228
235
|
do() {
|
|
229
236
|
this.node.evaluator = this.value
|
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.1.2",
|
|
4
|
+
"version": "2.1.3-beta.2",
|
|
5
5
|
"author": "Team Zincs (https://github.com/TeamZincs)",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
package/rpeChartCompiler.ts
CHANGED
|
@@ -22,13 +22,41 @@ type EasedStartNode<VT extends EventValueESType> = EventStartNode<VT> & { evalua
|
|
|
22
22
|
export class RPEChartCompiler {
|
|
23
23
|
sequenceMap: Map<EventNodeSequence<any>, EventNodeSequence<any>> = new Map();
|
|
24
24
|
interpolationStep: TimeT = [0, 1, 16];
|
|
25
|
-
|
|
25
|
+
deletesEmptyLines: boolean = true;
|
|
26
|
+
constructor(public chart: Chart) {
|
|
27
|
+
|
|
28
|
+
}
|
|
26
29
|
|
|
27
30
|
compileChart(): ChartDataRPE {
|
|
28
|
-
console.time("compileChart")
|
|
31
|
+
// console.time("compileChart")
|
|
29
32
|
const chart = this.chart;
|
|
30
33
|
const judgeLineGroups = chart.judgeLineGroups.map(group => group.name);
|
|
31
|
-
const
|
|
34
|
+
const filter = this.deletesEmptyLines ? (line: JudgeLine) => {
|
|
35
|
+
return line.nnLists.size > 0
|
|
36
|
+
|| line.hnLists.size > 0
|
|
37
|
+
|| line.eventLayers.length > 0
|
|
38
|
+
|| (["moveX", "moveY", "rotate", "alpha"] as const).some((evType) => {
|
|
39
|
+
const seq = line.eventLayers[0][evType];
|
|
40
|
+
let node = seq.head.next;
|
|
41
|
+
for (let i = 0; i < 2; i++) {
|
|
42
|
+
const endNode = node.next;
|
|
43
|
+
if (node.value !== 0) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
if (endNode.type === NodeType.TAIL) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
if (endNode.value !== 0) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
node = endNode.next;
|
|
53
|
+
}
|
|
54
|
+
return true; // 有超过两个的节点
|
|
55
|
+
})
|
|
56
|
+
} : () => true
|
|
57
|
+
const judgeLineList = chart.judgeLines
|
|
58
|
+
.filter(filter)
|
|
59
|
+
.map(line => this.compileJudgeLine(line));
|
|
32
60
|
const BPMList = chart.timeCalculator.dump();
|
|
33
61
|
const META: MetaData = {
|
|
34
62
|
RPEVersion: 1,
|
|
@@ -72,7 +100,7 @@ export class RPEChartCompiler {
|
|
|
72
100
|
}
|
|
73
101
|
|
|
74
102
|
|
|
75
|
-
console.timeEnd("compileChart");
|
|
103
|
+
// console.timeEnd("compileChart");
|
|
76
104
|
return {
|
|
77
105
|
BPMList,
|
|
78
106
|
META,
|
|
@@ -80,15 +108,15 @@ export class RPEChartCompiler {
|
|
|
80
108
|
judgeLineGroup: judgeLineGroups,
|
|
81
109
|
multiLineString: '',
|
|
82
110
|
multiScale: 1.0,
|
|
83
|
-
chartTime: chart.
|
|
84
|
-
kpaChartTime: chart.
|
|
111
|
+
chartTime: chart.rpeChartingSeconds,
|
|
112
|
+
kpaChartTime: chart.chartingSeconds,
|
|
85
113
|
};
|
|
86
114
|
}
|
|
87
115
|
|
|
88
116
|
compileJudgeLine(judgeLine: JudgeLine): JudgeLineDataRPE {
|
|
89
117
|
const chart = this.chart;
|
|
90
118
|
const notes = this.compileNNLists([...judgeLine.nnLists.values()], [...judgeLine.hnLists.values()]);
|
|
91
|
-
|
|
119
|
+
|
|
92
120
|
return {
|
|
93
121
|
notes: notes,
|
|
94
122
|
Group: chart.judgeLineGroups.indexOf(judgeLine.group),
|
|
@@ -139,10 +167,11 @@ export class RPEChartCompiler {
|
|
|
139
167
|
[0, 0, 0, 0],
|
|
140
168
|
easingLeft: isSegmented ? easing.left : 0.0,
|
|
141
169
|
easingRight: isSegmented ? easing.right : 1.0,
|
|
142
|
-
|
|
143
|
-
|
|
170
|
+
easingType: isSegmented ? (
|
|
171
|
+
easing.easing instanceof NormalEasing ? easing.easing.rpeId ?? 1 : null
|
|
172
|
+
) : (easing instanceof NormalEasing ?
|
|
144
173
|
easing.rpeId ?? 1 :
|
|
145
|
-
null,
|
|
174
|
+
null),
|
|
146
175
|
end,
|
|
147
176
|
endTime: endNode.time,
|
|
148
177
|
linkgroup: 0, // 假设默认值为 0
|
package/tsconfig.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
// Environment setup & latest features
|
|
4
|
-
"lib": ["ES2021"],
|
|
4
|
+
"lib": ["ES2021", "DOM"],
|
|
5
5
|
"target": "ES2016",
|
|
6
6
|
"module": "es2020",
|
|
7
7
|
"moduleDetection": "force",
|
|
8
8
|
"allowJs": true,
|
|
9
|
-
"outFile": "../dist/index.d.ts",
|
|
10
9
|
|
|
11
10
|
"emitDeclarationOnly": true,
|
|
12
11
|
"declaration": true,
|
|
@@ -18,6 +17,7 @@
|
|
|
18
17
|
"noUncheckedIndexedAccess": true,
|
|
19
18
|
|
|
20
19
|
// Some stricter flags (disabled by default)
|
|
20
|
+
"strict": false,
|
|
21
21
|
"noUnusedLocals": false,
|
|
22
22
|
"noUnusedParameters": false,
|
|
23
23
|
"noPropertyAccessFromIndexSignature": false
|
package/util.ts
CHANGED
|
@@ -34,9 +34,11 @@ export const hex2rgb = (hex: number): RGB => {
|
|
|
34
34
|
return [hex >> 16, hex >> 8 & 0xFF, hex & 0xFF]
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
//
|
|
37
|
+
// 5个2,4个3,3个5,7、11、13各一个
|
|
38
|
+
const DENO = 324324000;
|
|
39
|
+
|
|
38
40
|
export const numberToRatio = (num: number): [number, number] => {
|
|
39
|
-
return [Math.round(num *
|
|
41
|
+
return [Math.round(num * DENO), DENO]
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
|