kipphi 2.1.2 → 2.1.3-beta.1

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/index.js CHANGED
@@ -35,8 +35,9 @@ var rgb2hex = (rgb) => {
35
35
  var hex2rgb = (hex) => {
36
36
  return [hex >> 16, hex >> 8 & 255, hex & 255];
37
37
  };
38
+ var DENO = 324324000;
38
39
  var numberToRatio = (num) => {
39
- return [Math.round(num * 1e4), 1e4];
40
+ return [Math.round(num * DENO), DENO];
40
41
  };
41
42
  var toTimeString = (beaT) => `${beaT[0]}:${beaT[1]}/${beaT[2]}`;
42
43
 
@@ -77,6 +78,7 @@ var ERROR_IDS;
77
78
  ERROR_IDS2[ERROR_IDS2["NODES_NOT_CONTINUOUS"] = EASING | INVALID_USAGE | 1] = "NODES_NOT_CONTINUOUS";
78
79
  ERROR_IDS2[ERROR_IDS2["NODES_NOT_BELONG_TO_SAME_SEQUENCE"] = EASING | INVALID_USAGE | 2] = "NODES_NOT_BELONG_TO_SAME_SEQUENCE";
79
80
  ERROR_IDS2[ERROR_IDS2["NODES_HAS_ZERO_DELTA"] = EASING | INVALID_USAGE | 3] = "NODES_HAS_ZERO_DELTA";
81
+ ERROR_IDS2[ERROR_IDS2["TEMPLATE_EASING_CIRCULAR_REFERENCE"] = EASING | INVALID_USAGE | 4] = "TEMPLATE_EASING_CIRCULAR_REFERENCE";
80
82
  ERROR_IDS2[ERROR_IDS2["CANNOT_DIVIDE_EXPRESSION_EVALUATOR"] = EVALUATOR | INVALID_USAGE | 0] = "CANNOT_DIVIDE_EXPRESSION_EVALUATOR";
81
83
  ERROR_IDS2[ERROR_IDS2["MISSING_MACRO_EVALUATOR_KEY"] = EVALUATOR | INVALID_DATA | 0] = "MISSING_MACRO_EVALUATOR_KEY";
82
84
  ERROR_IDS2[ERROR_IDS2["MACRO_EVALUATOR_NOT_FOUND"] = EVALUATOR | INVALID_DATA | 1] = "MACRO_EVALUATOR_NOT_FOUND";
@@ -126,7 +128,8 @@ var ERRORS = {
126
128
  PARAMETRIC_MACRO_REQUIRES_PROTO_KEY: (pos) => `Parametric Macro requires key. At ${pos}`,
127
129
  MACRO_NOT_PARAMETRIC: (macroId, pos) => `Macro '${macroId}' is not parametric. At ${pos}`,
128
130
  EVENT_NODE_NOT_DENSE: (pos) => `EventNode is not dense. At ${pos}`,
129
- HOLD_HAS_NO_DURATION: () => `Hold should have a duration.`
131
+ HOLD_HAS_NO_DURATION: () => `Hold should have a duration.`,
132
+ TEMPLATE_EASING_CIRCULAR_REFERENCE: (temEasName) => `Template Easing '${temEasName}' has circular reference`
130
133
  };
131
134
 
132
135
  class KPAError extends Error {
@@ -548,6 +551,9 @@ class TemplateEasing extends Easing {
548
551
  this.name = name;
549
552
  }
550
553
  getValue(t) {
554
+ if (t === 1) {
555
+ return 1;
556
+ }
551
557
  const seq = this.eventNodeSequence;
552
558
  const delta = this.valueDelta;
553
559
  if (delta === 0) {
@@ -569,6 +575,28 @@ class TemplateEasing extends Easing {
569
575
  get headValue() {
570
576
  return this.eventNodeSequence.head.next.value;
571
577
  }
578
+ segmentedValueGetter(easingLeft, easingRight) {
579
+ return (t) => {
580
+ const leftValue = this.getValue(easingLeft);
581
+ const rightValue = this.getValue(easingRight);
582
+ const timeDelta = easingRight - easingLeft;
583
+ const delta = rightValue - leftValue;
584
+ if (delta === 0) {
585
+ return 0;
586
+ }
587
+ return (this.getValue(easingLeft + timeDelta * t) - leftValue) / delta;
588
+ };
589
+ }
590
+ static checkCircularReference(seq, template) {
591
+ const seq2 = template.eventNodeSequence;
592
+ if (seq === seq2) {
593
+ return true;
594
+ }
595
+ if (seq2.hasReferenceTo(seq)) {
596
+ return true;
597
+ }
598
+ return false;
599
+ }
572
600
  }
573
601
 
574
602
  class WrapperEasing extends Easing {
@@ -952,16 +980,24 @@ class EasedEvaluator extends Evaluator {
952
980
  super();
953
981
  this.easing = easing;
954
982
  }
955
- eval(startNode, beats) {
983
+ eval(startNode, beatsOrSeconds, timeCalculator) {
956
984
  const next = startNode.next;
957
- const timeDelta = TC2.getDelta(next.time, startNode.time);
958
- const current = beats - TC2.toBeats(startNode.time);
959
985
  const nextValue = startNode.next.value;
960
986
  const value = startNode.value;
961
987
  if (nextValue === value) {
962
988
  return value;
963
989
  }
964
- return this.convert(value, nextValue, this.easing.getValue(current / timeDelta));
990
+ if (timeCalculator) {
991
+ const startSecs = timeCalculator.toSeconds(TC2.toBeats(startNode.time));
992
+ const endSecs = timeCalculator.toSeconds(TC2.toBeats(next.time));
993
+ const current = beatsOrSeconds - startSecs;
994
+ const timeDelta = endSecs - startSecs;
995
+ return this.convert(value, nextValue, this.easing.getValue(current / timeDelta));
996
+ } else {
997
+ const timeDelta = TC2.getDelta(next.time, startNode.time);
998
+ const current = beatsOrSeconds - TC2.toBeats(startNode.time);
999
+ return this.convert(value, nextValue, this.easing.getValue(current / timeDelta));
1000
+ }
965
1001
  }
966
1002
  static getEvaluatorFromEasing(type, easing, interpretedAs) {
967
1003
  const easingIsNormal = easing instanceof NormalEasing;
@@ -1046,11 +1082,11 @@ class TextEasedEvaluator extends EasedEvaluator {
1046
1082
  if (interpretedAs === 2 /* float */) {
1047
1083
  const start = parseFloat(value);
1048
1084
  const delta = parseFloat(nextValue) - start;
1049
- return start + progress * delta + "";
1085
+ return (start + progress * delta).toFixed(3) + "";
1050
1086
  } else if (interpretedAs === 1 /* int */) {
1051
1087
  const start = parseInt(value);
1052
1088
  const delta = parseInt(nextValue) - start;
1053
- return start + Math.round(progress * delta) + "";
1089
+ return start + Math.floor(progress * delta) + "";
1054
1090
  } else if (value.startsWith(nextValue)) {
1055
1091
  const startLen = nextValue.length;
1056
1092
  const deltaLen = value.length - startLen;
@@ -1104,8 +1140,8 @@ class MacroEvaluator extends Evaluator {
1104
1140
  node.evaluator = this;
1105
1141
  this.consumers.set(node, this.compile(node, chart));
1106
1142
  }
1107
- eval(event, beats) {
1108
- return this.consumers.get(event).eval(event, beats);
1143
+ eval(event, beats, timeCalculator) {
1144
+ return this.consumers.get(event).eval(event, beats, timeCalculator);
1109
1145
  }
1110
1146
  dumpFor(node) {
1111
1147
  return {
@@ -1130,11 +1166,19 @@ class ExpressionEvaluator extends Evaluator {
1130
1166
  this.jsExpr = jsExpr;
1131
1167
  this.func = new Function("t", "return " + jsExpr);
1132
1168
  }
1133
- eval(startNode, beats) {
1134
- const next = startNode.next;
1135
- const timeDelta = TC2.getDelta(next.time, startNode.time);
1136
- const current = beats - TC2.toBeats(startNode.time);
1137
- return this.func(current / timeDelta);
1169
+ eval(startNode, beatsOrSecs, timeCalculator) {
1170
+ if (timeCalculator) {
1171
+ const startSecs = timeCalculator.toSeconds(TC2.toBeats(startNode.time));
1172
+ const endSecs = timeCalculator.toSeconds(TC2.toBeats(startNode.next.time));
1173
+ const current = beatsOrSecs - startSecs;
1174
+ const timeDelta = endSecs - startSecs;
1175
+ return this.func(current / timeDelta);
1176
+ } else {
1177
+ const next = startNode.next;
1178
+ const timeDelta = TC2.getDelta(next.time, startNode.time);
1179
+ const current = beatsOrSecs - TC2.toBeats(startNode.time);
1180
+ return this.func(current / timeDelta);
1181
+ }
1138
1182
  }
1139
1183
  dumpFor() {
1140
1184
  return {
@@ -1352,11 +1396,6 @@ class EventNode extends EventNodeLike {
1352
1396
  return ret;
1353
1397
  }
1354
1398
  static getEasing(data, templates, notSegmented = false) {
1355
- const left = data.easingLeft;
1356
- const right = data.easingRight;
1357
- if (!notSegmented && (left && right) && (left !== 0 || right !== 1)) {
1358
- return new SegmentedEasing(EventNode.getEasing(data, templates, true), left, right);
1359
- }
1360
1399
  if (data.bezier) {
1361
1400
  const bp = data.bezierPoints;
1362
1401
  const easing = new BezierEasing([bp[0], bp[1]], [bp[2], bp[3]]);
@@ -1563,11 +1602,11 @@ class EventStartNode extends EventNode {
1563
1602
  linkedMacro: [...this.linkedMacros].map((macro) => macro.dumpLinkForNode(this))
1564
1603
  };
1565
1604
  }
1566
- getValueAt(beats) {
1605
+ getValueAt(beatsOrSecs, timeCalculator) {
1567
1606
  if (this.next.type === 1 /* TAIL */) {
1568
1607
  return this.value;
1569
1608
  }
1570
- return this.evaluator.eval(this, beats);
1609
+ return this.evaluator.eval(this, beatsOrSecs, timeCalculator);
1571
1610
  }
1572
1611
  getSpeedValueAt(beats) {
1573
1612
  if (this.next.type === 1 /* TAIL */) {
@@ -1845,6 +1884,9 @@ class EventNodeSequence {
1845
1884
  getValueAt(beats, usePrev = false) {
1846
1885
  return this.getNodeAt(beats, usePrev).getValueAt(beats);
1847
1886
  }
1887
+ getValueAtBySecs(beats, seconds, timeCalculator, usePrev = false) {
1888
+ return this.getNodeAt(beats, usePrev).getValueAt(seconds, timeCalculator);
1889
+ }
1848
1890
  getFloorPositionAt(beats, timeCalculator) {
1849
1891
  const node = this.getNodeAt(beats);
1850
1892
  const value = node.getLocalFloorPos(beats, timeCalculator) + node.floorPosition;
@@ -1861,7 +1903,7 @@ class EventNodeSequence {
1861
1903
  const prevStart = node.previous.previous;
1862
1904
  currentFP = prevStart.floorPosition + prevStart.getFullLocalFloorPos(tc);
1863
1905
  } else {
1864
- currentFP = 0;
1906
+ node.floorPosition = currentFP = 0;
1865
1907
  }
1866
1908
  while (true) {
1867
1909
  const canBeEnd = node.next;
@@ -1980,24 +2022,40 @@ class EventNodeSequence {
1980
2022
  currentNode = currentNode.next.next;
1981
2023
  }
1982
2024
  }
2025
+ hasReferenceTo(seq) {
2026
+ let node = this.head.next;
2027
+ while (true) {
2028
+ const endNode = node.next;
2029
+ if (endNode.type === 1 /* TAIL */) {
2030
+ break;
2031
+ }
2032
+ const evaluator = node.evaluator;
2033
+ if (evaluator instanceof EasedEvaluator && evaluator.easing instanceof TemplateEasing) {
2034
+ if (TemplateEasing.checkCircularReference(seq, evaluator.easing)) {
2035
+ return true;
2036
+ }
2037
+ }
2038
+ node = endNode.next;
2039
+ }
2040
+ }
1983
2041
  }
1984
2042
  // src/note.ts
1985
2043
  var notePropTypes = {
1986
2044
  above: "boolean",
1987
- alpha: "number",
2045
+ alpha: "int[0,255]",
1988
2046
  endTime: ["number", "number", "number"],
1989
2047
  isFake: "boolean",
1990
2048
  positionX: "number",
1991
- size: "number",
2049
+ size: "number(0,+)",
1992
2050
  speed: "number",
1993
2051
  startTime: ["number", "number", "number"],
1994
- type: "number",
1995
- visibleTime: "number",
1996
- visibleBeats: "number",
2052
+ type: "int[1,4]",
2053
+ visibleTime: "number(0,+)",
2054
+ visibleBeats: "number(0,+)",
1997
2055
  yOffset: "number",
1998
- tint: ["number", "number", "number"],
1999
- tintHitEffects: ["number", "number", "number"],
2000
- judgeSize: "number"
2056
+ tint: ["int[0,255]", "int[0,255]", "int[0,255]"],
2057
+ tintHitEffects: ["int[0,255]", "int[0,255]", "int[0,255]"],
2058
+ judgeSize: "number(0,+)"
2001
2059
  };
2002
2060
 
2003
2061
  class Note {
@@ -2030,9 +2088,10 @@ class Note {
2030
2088
  this.visibleTime = data.visibleTime;
2031
2089
  this.yOffset = data.absoluteYOffset ?? data.yOffset * this.speed;
2032
2090
  this.visibleBeats = data.visibleBeats;
2033
- this.tint = data.tint ? rgb2hex(data.tint) : undefined;
2091
+ const color = data.tint ?? data.color;
2092
+ this.tint = color ? rgb2hex(color) : undefined;
2034
2093
  this.tintHitEffects = data.tintHitEffects ? rgb2hex(data.tintHitEffects) : undefined;
2035
- this.judgeSize = data.judgeSize ?? this.size;
2094
+ this.judgeSize = data.judgeSize ?? data.judgeArea ?? this.size;
2036
2095
  }
2037
2096
  static fromKPAJSON(data, timeCalculator) {
2038
2097
  const note = new Note(data);
@@ -2079,7 +2138,8 @@ class Note {
2079
2138
  yOffset: this.yOffset / this.speed,
2080
2139
  speed: this.speed,
2081
2140
  tint: this.tint !== undefined ? hex2rgb(this.tint) : undefined,
2082
- tintHitEffects: this.tint !== undefined ? hex2rgb(this.tintHitEffects) : undefined
2141
+ tintHitEffects: this.tint !== undefined ? hex2rgb(this.tintHitEffects) : undefined,
2142
+ judgeArea: this.judgeSize
2083
2143
  };
2084
2144
  }
2085
2145
  dumpKPA() {
@@ -2636,17 +2696,17 @@ class JudgeLine {
2636
2696
  line.speedSequence = speedSequences[0];
2637
2697
  chart.registerEventNodeSequence(4 /* speed */, `#${id}.speed`, speedSequences[0]);
2638
2698
  }
2699
+ if (data.extended?.scaleXEvents) {
2700
+ line.extendedLayer.scaleX = createExtendedSequence(7 /* scaleX */, data.extended.scaleXEvents);
2701
+ } else {
2702
+ line.extendedLayer.scaleX = chart.createEventNodeSequence(7 /* scaleX */, `#${id}.ex.scaleX`);
2703
+ }
2704
+ if (data.extended?.scaleYEvents) {
2705
+ line.extendedLayer.scaleY = createExtendedSequence(8 /* scaleY */, data.extended.scaleYEvents);
2706
+ } else {
2707
+ line.extendedLayer.scaleY = chart.createEventNodeSequence(8 /* scaleY */, `#${id}.ex.scaleY`);
2708
+ }
2639
2709
  if (data.extended) {
2640
- if (data.extended.scaleXEvents) {
2641
- line.extendedLayer.scaleX = createExtendedSequence(7 /* scaleX */, data.extended.scaleXEvents);
2642
- } else {
2643
- line.extendedLayer.scaleX = chart.createEventNodeSequence(7 /* scaleX */, `#${id}.ex.scaleX`);
2644
- }
2645
- if (data.extended.scaleYEvents) {
2646
- line.extendedLayer.scaleY = createExtendedSequence(8 /* scaleY */, data.extended.scaleYEvents);
2647
- } else {
2648
- line.extendedLayer.scaleY = chart.createEventNodeSequence(8 /* scaleY */, `#${id}.ex.scaleY`);
2649
- }
2650
2710
  if (data.extended.textEvents) {
2651
2711
  line.extendedLayer.text = createExtendedSequence(9 /* text */, data.extended.textEvents);
2652
2712
  }
@@ -2815,7 +2875,7 @@ class JudgeLine {
2815
2875
  if (lineMonotonicity === 0 /* increasing */ && startY >= 0 && endY > 0) {
2816
2876
  return result;
2817
2877
  }
2818
- const computeTime = (speed, currentPos, fore) => timeCalculator.secondsToBeats(currentPos / (speed * 120) + timeCalculator.toSeconds(fore));
2878
+ const computeTime = (speed, currentPos, fore) => speed * currentPos <= 0 ? fore : timeCalculator.secondsToBeats(currentPos / (speed * 120) + timeCalculator.toSeconds(fore));
2819
2879
  while (true) {
2820
2880
  const thisTime = TC2.toBeats(startNode.time);
2821
2881
  const endNode = startNode.next;
@@ -2823,17 +2883,21 @@ class JudgeLine {
2823
2883
  if (endNode.type === 1 /* TAIL */) {
2824
2884
  const thisPosY2 = startNode.floorPosition - currentJudgeLineFloorPos;
2825
2885
  const thisSpeed2 = startNode.value;
2826
- const inf = thisSpeed2 > 0 ? Infinity : thisSpeed2 < 0 ? -Infinity : thisPosY2;
2827
2886
  if (range[0] === undefined) {
2828
- if (thisPosY2 < startY && startY <= inf || thisPosY2 >= endY && endY > inf) {
2829
- range[0] = computeTime(thisSpeed2, (thisPosY2 < inf ? startY : endY) - thisPosY2, thisTime);
2830
- } else if (thisSpeed2 === 0) {
2831
- range[0] = 0;
2887
+ if (thisSpeed2 > 0 && endY > thisPosY2) {
2888
+ range[0] = computeTime(thisSpeed2, startY - thisPosY2, thisTime);
2889
+ } else if (thisSpeed2 < 0 && startY < thisPosY2) {
2890
+ range[0] = computeTime(thisSpeed2, endY - thisPosY2, thisTime);
2891
+ } else if (thisSpeed2 === 0 && startY <= thisPosY2 && thisPosY2 <= endY) {
2892
+ range[0] = thisTime;
2832
2893
  }
2833
2894
  }
2834
2895
  if (range[0] !== undefined) {
2835
- if (thisPosY2 < endY && endY <= inf || thisPosY2 >= startY && startY > inf) {
2836
- range[1] = computeTime(thisSpeed2, (thisPosY2 > inf ? startY : endY) - thisPosY2, thisTime);
2896
+ if (thisSpeed2 > 0 && endY > thisPosY2) {
2897
+ range[1] = computeTime(thisSpeed2, endY - thisPosY2, thisTime);
2898
+ result.push(range);
2899
+ } else if (thisSpeed2 < 0 && startY < thisPosY2) {
2900
+ range[1] = computeTime(thisSpeed2, startY - thisPosY2, thisTime);
2837
2901
  result.push(range);
2838
2902
  } else if (thisSpeed2 === 0) {
2839
2903
  range[1] = Infinity;
@@ -2846,7 +2910,7 @@ class JudgeLine {
2846
2910
  const thisPosY = startNode.floorPosition - currentJudgeLineFloorPos;
2847
2911
  const nextPosY = nextStart.floorPosition - currentJudgeLineFloorPos;
2848
2912
  let thisSpeed = startNode.value;
2849
- let nextSpeed = nextStart.value;
2913
+ let nextSpeed = endNode.value;
2850
2914
  if (Math.abs(thisSpeed) < 0.00000001) {
2851
2915
  thisSpeed = 0;
2852
2916
  }
@@ -2858,15 +2922,13 @@ class JudgeLine {
2858
2922
  const zeroPosY = speedSequence.getFloorPositionAt(zeroTime, timeCalculator) - currentJudgeLineFloorPos;
2859
2923
  const zeroSpeed = 0;
2860
2924
  if (range[0] === undefined) {
2861
- if (thisPosY < startY && startY <= zeroPosY || thisPosY > endY && endY >= zeroPosY) {
2862
- range[0] = thisSpeed !== zeroSpeed ? thisTime : computeTime(thisSpeed, (thisPosY < zeroPosY ? startY : endY) - thisPosY, thisTime);
2863
- } else if (startY <= thisPosY && thisPosY <= endY) {
2925
+ if (thisSpeed > 0 && endY >= thisPosY || thisSpeed < 0 && startY <= thisPosY) {
2864
2926
  range[0] = thisTime;
2865
2927
  }
2866
2928
  }
2867
2929
  if (range[0] !== undefined) {
2868
- if (thisPosY < endY && endY <= zeroPosY || thisPosY > startY && startY >= zeroPosY) {
2869
- range[1] = thisSpeed !== zeroSpeed ? zeroTime : computeTime(thisSpeed, (thisPosY > zeroPosY ? startY : endY) - thisPosY, thisTime);
2930
+ if (thisSpeed > 0 && endY <= zeroPosY || thisSpeed < 0 && startY >= zeroPosY) {
2931
+ range[1] = zeroTime;
2870
2932
  result.push(range);
2871
2933
  if (lineMonotonicity !== 2 /* swinging */) {
2872
2934
  return result;
@@ -2875,15 +2937,13 @@ class JudgeLine {
2875
2937
  }
2876
2938
  }
2877
2939
  if (range[0] === undefined) {
2878
- if (zeroPosY < startY && startY <= nextPosY || zeroPosY > endY && endY >= nextPosY) {
2879
- range[0] = zeroSpeed !== nextSpeed ? zeroTime : computeTime(nextSpeed, (zeroPosY < nextPosY ? startY : endY) - zeroPosY, zeroTime);
2880
- } else if (startY <= zeroPosY && zeroPosY <= endY) {
2940
+ if (nextSpeed > 0 && endY >= zeroPosY || nextSpeed < 0 && startY <= zeroPosY) {
2881
2941
  range[0] = zeroTime;
2882
2942
  }
2883
2943
  }
2884
2944
  if (range[0] !== undefined) {
2885
- if (zeroPosY < endY && endY <= nextPosY || zeroPosY > startY && startY >= nextPosY) {
2886
- range[1] = zeroSpeed !== nextSpeed ? nextTime : computeTime(nextSpeed, (zeroPosY > nextPosY ? startY : endY) - zeroPosY, zeroTime);
2945
+ if (nextSpeed > 0 && endY <= nextPosY || nextSpeed < 0 && startY >= nextPosY) {
2946
+ range[1] = nextTime;
2887
2947
  result.push(range);
2888
2948
  if (lineMonotonicity !== 2 /* swinging */) {
2889
2949
  return result;
@@ -2893,15 +2953,24 @@ class JudgeLine {
2893
2953
  }
2894
2954
  } else {
2895
2955
  if (range[0] === undefined) {
2896
- if (thisPosY < startY && startY <= nextPosY || thisPosY > endY && endY >= nextPosY) {
2897
- range[0] = thisSpeed !== nextSpeed ? thisTime : computeTime(thisSpeed, (thisPosY < nextPosY ? startY : endY) - thisPosY, thisTime);
2898
- } else if (startY <= thisPosY && thisPosY <= endY) {
2956
+ if (thisSpeed > 0 && endY >= thisPosY && startY < nextPosY) {
2957
+ range[0] = thisSpeed !== nextSpeed ? thisTime : computeTime(thisSpeed, startY - thisPosY, thisTime);
2958
+ } else if (thisSpeed < 0 && startY <= thisPosY && endY > nextPosY) {
2959
+ range[0] = thisSpeed !== nextSpeed ? thisTime : computeTime(thisSpeed, endY - thisPosY, thisTime);
2960
+ } else if (thisSpeed === 0 && startY <= thisPosY && thisPosY <= endY) {
2899
2961
  range[0] = thisTime;
2900
2962
  }
2901
2963
  }
2902
2964
  if (range[0] !== undefined) {
2903
- if (thisPosY < endY && endY <= nextPosY || thisPosY > startY && startY >= nextPosY) {
2904
- range[1] = thisSpeed !== nextSpeed ? nextTime : computeTime(thisSpeed, (thisPosY > nextPosY ? startY : endY) - thisPosY, thisTime);
2965
+ if (thisSpeed > 0 && endY <= nextPosY) {
2966
+ range[1] = thisSpeed !== nextSpeed ? nextTime : computeTime(thisSpeed, endY - thisPosY, thisTime);
2967
+ result.push(range);
2968
+ if (lineMonotonicity !== 2 /* swinging */) {
2969
+ return result;
2970
+ }
2971
+ range = [undefined, undefined];
2972
+ } else if (thisSpeed < 0 && startY >= thisPosY) {
2973
+ range[1] = thisSpeed !== nextSpeed ? nextTime : computeTime(thisSpeed, startY - thisPosY, thisTime);
2905
2974
  result.push(range);
2906
2975
  if (lineMonotonicity !== 2 /* swinging */) {
2907
2976
  return result;
@@ -2934,6 +3003,18 @@ class JudgeLine {
2934
3003
  }
2935
3004
  return current;
2936
3005
  }
3006
+ getStackedValueBySeconds(type, beats, seconds, timeCalculator, usePrev = false) {
3007
+ const length = this.eventLayers.length;
3008
+ let current = 0;
3009
+ for (let index = 0;index < length; index++) {
3010
+ const layer = this.eventLayers[index];
3011
+ if (!layer || !layer[type]) {
3012
+ break;
3013
+ }
3014
+ current += layer[type].getValueAtBySecs(beats, seconds, timeCalculator, usePrev);
3015
+ }
3016
+ return current;
3017
+ }
2937
3018
  getNNList(speed, yOffset, isHold, initsJump) {
2938
3019
  const lists = isHold ? this.hnLists : this.nnLists;
2939
3020
  const medianYOffset = getRangeMedian(yOffset);
@@ -3190,6 +3271,9 @@ class TimeCalculator {
3190
3271
  duration;
3191
3272
  constructor() {}
3192
3273
  initSequence() {
3274
+ if (!this.bpmList || !this.duration) {
3275
+ throw new Error("TimeCalculator: bpmList and duration must be set before initSequence");
3276
+ }
3193
3277
  const bpmList = this.bpmList;
3194
3278
  this.bpmSequence = new BPMSequence(bpmList, this.duration);
3195
3279
  }
@@ -3211,7 +3295,7 @@ class TimeCalculator {
3211
3295
  }
3212
3296
  }
3213
3297
  // src/version.ts
3214
- var VERSION = 212;
3298
+ var VERSION = 213;
3215
3299
  var SCHEMA = "https://cdn.jsdelivr.net/npm/kipphi@2.1.0/chartType2.schema.json";
3216
3300
 
3217
3301
  // src/chart.ts
@@ -3251,7 +3335,7 @@ class Chart {
3251
3335
  }
3252
3336
  static fromRPEJSON(data, duration) {
3253
3337
  const chart = new Chart;
3254
- chart.judgeLineGroups = data.judgeLineGroup.map((group) => new JudgeLineGroup(group));
3338
+ chart.judgeLineGroups = (data.judgeLineGroup || ["Default"]).map((group) => new JudgeLineGroup(group));
3255
3339
  chart.name = data.META.name;
3256
3340
  chart.level = data.META.level;
3257
3341
  chart.offset = data.META.offset;
@@ -3342,6 +3426,13 @@ class Chart {
3342
3426
  }
3343
3427
  chart.templateEasingLib.implement(easingData.name, sequence);
3344
3428
  }
3429
+ for (let i = 0;i < len; i++) {
3430
+ const easingData = templateEasings[i];
3431
+ const sequence = chart.sequenceMap.get(easingData.content);
3432
+ if (sequence.hasReferenceTo(sequence)) {
3433
+ throw err.TEMPLATE_EASING_CIRCULAR_REFERENCE(easingData.name);
3434
+ }
3435
+ }
3345
3436
  chart.templateEasingLib.check();
3346
3437
  for (const lineData of data.orphanLines) {
3347
3438
  const line = JudgeLine.fromKPAJSON(data.version, chart, lineData.id, lineData, chart.templateEasingLib, chart.timeCalculator);
@@ -4156,6 +4247,13 @@ class EventNodeEvaluatorChangeOperation extends Operation {
4156
4247
  this.node = node;
4157
4248
  this.value = value;
4158
4249
  this.originalValue = this.node.evaluator;
4250
+ const seq = node.parentSeq;
4251
+ if (seq.type === 5 /* easing */ && value instanceof EasedEvaluator && value.easing instanceof TemplateEasing) {
4252
+ const circular = TemplateEasing.checkCircularReference(seq, value.easing);
4253
+ if (circular) {
4254
+ throw err.TEMPLATE_EASING_CIRCULAR_REFERENCE(value.easing.name);
4255
+ }
4256
+ }
4159
4257
  }
4160
4258
  do() {
4161
4259
  this.node.evaluator = this.value;
@@ -5194,14 +5292,34 @@ class RPEChartCompiler {
5194
5292
  chart;
5195
5293
  sequenceMap = new Map;
5196
5294
  interpolationStep = [0, 1, 16];
5295
+ deletesEmptyLines = true;
5197
5296
  constructor(chart2) {
5198
5297
  this.chart = chart2;
5199
5298
  }
5200
5299
  compileChart() {
5201
- console.time("compileChart");
5202
5300
  const chart2 = this.chart;
5203
5301
  const judgeLineGroups = chart2.judgeLineGroups.map((group) => group.name);
5204
- const judgeLineList = chart2.judgeLines.map((line2) => this.compileJudgeLine(line2));
5302
+ const filter = this.deletesEmptyLines ? (line2) => {
5303
+ return line2.nnLists.size > 0 || line2.hnLists.size > 0 || line2.eventLayers.length > 0 || ["moveX", "moveY", "rotate", "alpha"].some((evType) => {
5304
+ const seq = line2.eventLayers[0][evType];
5305
+ let node = seq.head.next;
5306
+ for (let i = 0;i < 2; i++) {
5307
+ const endNode = node.next;
5308
+ if (node.value !== 0) {
5309
+ return true;
5310
+ }
5311
+ if (endNode.type === 1 /* TAIL */) {
5312
+ return false;
5313
+ }
5314
+ if (endNode.value !== 0) {
5315
+ return true;
5316
+ }
5317
+ node = endNode.next;
5318
+ }
5319
+ return true;
5320
+ });
5321
+ } : () => true;
5322
+ const judgeLineList = chart2.judgeLines.filter(filter).map((line2) => this.compileJudgeLine(line2));
5205
5323
  const BPMList = chart2.timeCalculator.dump();
5206
5324
  const META = {
5207
5325
  RPEVersion: 1,
@@ -5240,7 +5358,6 @@ class RPEChartCompiler {
5240
5358
  lineData.attachUI = uiName;
5241
5359
  }
5242
5360
  }
5243
- console.timeEnd("compileChart");
5244
5361
  return {
5245
5362
  BPMList,
5246
5363
  META,
@@ -5248,8 +5365,8 @@ class RPEChartCompiler {
5248
5365
  judgeLineGroup: judgeLineGroups,
5249
5366
  multiLineString: "",
5250
5367
  multiScale: 1,
5251
- chartTime: chart2.rpeChartingTime * 60,
5252
- kpaChartTime: chart2.chartingTime
5368
+ chartTime: chart2.rpeChartingSeconds,
5369
+ kpaChartTime: chart2.chartingSeconds
5253
5370
  };
5254
5371
  }
5255
5372
  compileJudgeLine(judgeLine) {
@@ -5296,7 +5413,7 @@ class RPEChartCompiler {
5296
5413
  bezierPoints: innerEasing instanceof BezierEasing ? [innerEasing.cp1[0], innerEasing.cp1[1], innerEasing.cp2[0], innerEasing.cp2[1]] : [0, 0, 0, 0],
5297
5414
  easingLeft: isSegmented ? easing.left : 0,
5298
5415
  easingRight: isSegmented ? easing.right : 1,
5299
- easingType: easing instanceof NormalEasing ? easing.rpeId ?? 1 : null,
5416
+ easingType: isSegmented ? easing.easing instanceof NormalEasing ? easing.easing.rpeId ?? 1 : null : easing instanceof NormalEasing ? easing.rpeId ?? 1 : null,
5300
5417
  end,
5301
5418
  endTime: endNode.time,
5302
5419
  linkgroup: 0,