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/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
- if (thisPosY < startY && startY <= inf || thisPosY >= endY && endY > inf) {
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
- (thisPosY < inf ? startY : endY) - thisPosY,
605
+ startY - thisPosY,
594
606
  thisTime)
595
- } else if (thisSpeed === 0) {
596
- range[0] = 0;
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
- if (thisPosY < endY && endY <= inf || thisPosY >= startY && startY > inf) {
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
- (thisPosY > inf ? startY : endY) - thisPosY,
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 = nextStart.value;
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 (thisPosY < startY && startY <= zeroPosY || thisPosY > endY && endY >= zeroPosY) {
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 (thisPosY < endY && endY <= zeroPosY || thisPosY > startY && startY >= zeroPosY) {
653
- range[1] = thisSpeed !== zeroSpeed ? zeroTime : computeTime(
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 (zeroPosY < startY && startY <= nextPosY || zeroPosY > endY && endY >= nextPosY) {
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 (zeroPosY < endY && endY <= nextPosY || zeroPosY > startY && startY >= nextPosY) {
678
- range[1] = zeroSpeed !== nextSpeed ? nextTime : computeTime(
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 (thisPosY < startY && startY <= nextPosY || thisPosY > endY && endY >= nextPosY) {
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
- (thisPosY < nextPosY ? startY : endY) - thisPosY, thisTime)
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 (thisPosY < endY && endY <= nextPosY || thisPosY > startY && startY >= nextPosY) {
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
- (thisPosY > nextPosY ? startY : endY) - thisPosY, thisTime)
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: "number",
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: "number",
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: ["number", "number", "number"],
35
- tintHitEffects: ["number", "number", "number"],
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
- this.tint = data.tint ? rgb2hex(data.tint) : undefined;
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
- this.judgeSize = data.judgeSize ?? this.size;
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.tint !== undefined ? hex2rgb(this.tintHitEffects) : undefined
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.tint !== undefined ? hex2rgb(this.tintHitEffects) : undefined,
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
+ }
@@ -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": {
@@ -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
- constructor(public chart: Chart) {}
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 judgeLineList = chart.judgeLines.map(line => this.compileJudgeLine(line));
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.rpeChartingTime * 60,
84
- kpaChartTime: chart.chartingTime,
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
- // @ts-expect-error 缓动为贝塞尔型时可以为null
143
- easingType: easing instanceof NormalEasing ?
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 * 10000), 10000]
41
+ return [Math.round(num * DENO), DENO]
40
42
  }
41
43
 
42
44
 
package/version.ts CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  /// #declaration:global
4
4
 
5
- export const VERSION = 212;
5
+ export const VERSION = 213;
6
6
  export const SCHEMA = "https://cdn.jsdelivr.net/npm/kipphi@2.1.0/chartType2.schema.json"
7
7
 
8
8