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/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,8 @@ 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";
82
+ ERROR_IDS2[ERROR_IDS2["EASING_DELTA_CANNOT_BE_ZERO"] = EASING | INVALID_USAGE | 5] = "EASING_DELTA_CANNOT_BE_ZERO";
80
83
  ERROR_IDS2[ERROR_IDS2["CANNOT_DIVIDE_EXPRESSION_EVALUATOR"] = EVALUATOR | INVALID_USAGE | 0] = "CANNOT_DIVIDE_EXPRESSION_EVALUATOR";
81
84
  ERROR_IDS2[ERROR_IDS2["MISSING_MACRO_EVALUATOR_KEY"] = EVALUATOR | INVALID_DATA | 0] = "MISSING_MACRO_EVALUATOR_KEY";
82
85
  ERROR_IDS2[ERROR_IDS2["MACRO_EVALUATOR_NOT_FOUND"] = EVALUATOR | INVALID_DATA | 1] = "MACRO_EVALUATOR_NOT_FOUND";
@@ -126,7 +129,9 @@ var ERRORS = {
126
129
  PARAMETRIC_MACRO_REQUIRES_PROTO_KEY: (pos) => `Parametric Macro requires key. At ${pos}`,
127
130
  MACRO_NOT_PARAMETRIC: (macroId, pos) => `Macro '${macroId}' is not parametric. At ${pos}`,
128
131
  EVENT_NODE_NOT_DENSE: (pos) => `EventNode is not dense. At ${pos}`,
129
- HOLD_HAS_NO_DURATION: () => `Hold should have a duration.`
132
+ HOLD_HAS_NO_DURATION: () => `Hold should have a duration.`,
133
+ TEMPLATE_EASING_CIRCULAR_REFERENCE: (temEasName) => `Template Easing '${temEasName}' has circular reference`,
134
+ EASING_DELTA_CANNOT_BE_ZERO: (seqName, time) => `Easing delta cannot be zero. (at ${seqName}, ${toTimeString(time)}`
130
135
  };
131
136
 
132
137
  class KPAError extends Error {
@@ -548,6 +553,9 @@ class TemplateEasing extends Easing {
548
553
  this.name = name;
549
554
  }
550
555
  getValue(t) {
556
+ if (t === 1) {
557
+ return 1;
558
+ }
551
559
  const seq = this.eventNodeSequence;
552
560
  const delta = this.valueDelta;
553
561
  if (delta === 0) {
@@ -569,6 +577,28 @@ class TemplateEasing extends Easing {
569
577
  get headValue() {
570
578
  return this.eventNodeSequence.head.next.value;
571
579
  }
580
+ segmentedValueGetter(easingLeft, easingRight) {
581
+ return (t) => {
582
+ const leftValue = this.getValue(easingLeft);
583
+ const rightValue = this.getValue(easingRight);
584
+ const timeDelta = easingRight - easingLeft;
585
+ const delta = rightValue - leftValue;
586
+ if (delta === 0) {
587
+ return 0;
588
+ }
589
+ return (this.getValue(easingLeft + timeDelta * t) - leftValue) / delta;
590
+ };
591
+ }
592
+ static checkCircularReference(seq, template) {
593
+ const seq2 = template.eventNodeSequence;
594
+ if (seq === seq2) {
595
+ return true;
596
+ }
597
+ if (seq2.hasReferenceTo(seq)) {
598
+ return true;
599
+ }
600
+ return false;
601
+ }
572
602
  }
573
603
 
574
604
  class WrapperEasing extends Easing {
@@ -952,16 +982,24 @@ class EasedEvaluator extends Evaluator {
952
982
  super();
953
983
  this.easing = easing;
954
984
  }
955
- eval(startNode, beats) {
985
+ eval(startNode, beatsOrSeconds, timeCalculator) {
956
986
  const next = startNode.next;
957
- const timeDelta = TC2.getDelta(next.time, startNode.time);
958
- const current = beats - TC2.toBeats(startNode.time);
959
987
  const nextValue = startNode.next.value;
960
988
  const value = startNode.value;
961
989
  if (nextValue === value) {
962
990
  return value;
963
991
  }
964
- return this.convert(value, nextValue, this.easing.getValue(current / timeDelta));
992
+ if (timeCalculator) {
993
+ const startSecs = timeCalculator.toSeconds(TC2.toBeats(startNode.time));
994
+ const endSecs = timeCalculator.toSeconds(TC2.toBeats(next.time));
995
+ const current = beatsOrSeconds - startSecs;
996
+ const timeDelta = endSecs - startSecs;
997
+ return this.convert(value, nextValue, this.easing.getValue(current / timeDelta));
998
+ } else {
999
+ const timeDelta = TC2.getDelta(next.time, startNode.time);
1000
+ const current = beatsOrSeconds - TC2.toBeats(startNode.time);
1001
+ return this.convert(value, nextValue, this.easing.getValue(current / timeDelta));
1002
+ }
965
1003
  }
966
1004
  static getEvaluatorFromEasing(type, easing, interpretedAs) {
967
1005
  const easingIsNormal = easing instanceof NormalEasing;
@@ -1046,11 +1084,11 @@ class TextEasedEvaluator extends EasedEvaluator {
1046
1084
  if (interpretedAs === 2 /* float */) {
1047
1085
  const start = parseFloat(value);
1048
1086
  const delta = parseFloat(nextValue) - start;
1049
- return start + progress * delta + "";
1087
+ return (start + progress * delta).toFixed(3) + "";
1050
1088
  } else if (interpretedAs === 1 /* int */) {
1051
1089
  const start = parseInt(value);
1052
1090
  const delta = parseInt(nextValue) - start;
1053
- return start + Math.round(progress * delta) + "";
1091
+ return start + Math.floor(progress * delta) + "";
1054
1092
  } else if (value.startsWith(nextValue)) {
1055
1093
  const startLen = nextValue.length;
1056
1094
  const deltaLen = value.length - startLen;
@@ -1104,8 +1142,8 @@ class MacroEvaluator extends Evaluator {
1104
1142
  node.evaluator = this;
1105
1143
  this.consumers.set(node, this.compile(node, chart));
1106
1144
  }
1107
- eval(event, beats) {
1108
- return this.consumers.get(event).eval(event, beats);
1145
+ eval(event, beats, timeCalculator) {
1146
+ return this.consumers.get(event).eval(event, beats, timeCalculator);
1109
1147
  }
1110
1148
  dumpFor(node) {
1111
1149
  return {
@@ -1130,11 +1168,19 @@ class ExpressionEvaluator extends Evaluator {
1130
1168
  this.jsExpr = jsExpr;
1131
1169
  this.func = new Function("t", "return " + jsExpr);
1132
1170
  }
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);
1171
+ eval(startNode, beatsOrSecs, timeCalculator) {
1172
+ if (timeCalculator) {
1173
+ const startSecs = timeCalculator.toSeconds(TC2.toBeats(startNode.time));
1174
+ const endSecs = timeCalculator.toSeconds(TC2.toBeats(startNode.next.time));
1175
+ const current = beatsOrSecs - startSecs;
1176
+ const timeDelta = endSecs - startSecs;
1177
+ return this.func(current / timeDelta);
1178
+ } else {
1179
+ const next = startNode.next;
1180
+ const timeDelta = TC2.getDelta(next.time, startNode.time);
1181
+ const current = beatsOrSecs - TC2.toBeats(startNode.time);
1182
+ return this.func(current / timeDelta);
1183
+ }
1138
1184
  }
1139
1185
  dumpFor() {
1140
1186
  return {
@@ -1352,11 +1398,6 @@ class EventNode extends EventNodeLike {
1352
1398
  return ret;
1353
1399
  }
1354
1400
  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
1401
  if (data.bezier) {
1361
1402
  const bp = data.bezierPoints;
1362
1403
  const easing = new BezierEasing([bp[0], bp[1]], [bp[2], bp[3]]);
@@ -1563,11 +1604,11 @@ class EventStartNode extends EventNode {
1563
1604
  linkedMacro: [...this.linkedMacros].map((macro) => macro.dumpLinkForNode(this))
1564
1605
  };
1565
1606
  }
1566
- getValueAt(beats) {
1607
+ getValueAt(beatsOrSecs, timeCalculator) {
1567
1608
  if (this.next.type === 1 /* TAIL */) {
1568
1609
  return this.value;
1569
1610
  }
1570
- return this.evaluator.eval(this, beats);
1611
+ return this.evaluator.eval(this, beatsOrSecs, timeCalculator);
1571
1612
  }
1572
1613
  getSpeedValueAt(beats) {
1573
1614
  if (this.next.type === 1 /* TAIL */) {
@@ -1732,6 +1773,10 @@ class EventNodeSequence {
1732
1773
  for (let index = 0;index < length; index++) {
1733
1774
  const event = data[index];
1734
1775
  const [start, end] = chart.createEventFromData(event, valueType, `${pos}.events[${index}]`);
1776
+ const evaluator = start.evaluator;
1777
+ if (evaluator instanceof EasedEvaluator && evaluator.easing instanceof SegmentedEasing && evaluator.easing.easing instanceof TemplateEasing) {
1778
+ chart.segmentedTemplates.set(evaluator.easing, [pos, start.time]);
1779
+ }
1735
1780
  if (TC2.lt(event.startTime, lastEndTime)) {
1736
1781
  err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}] and the previous`).warn();
1737
1782
  }
@@ -1845,6 +1890,9 @@ class EventNodeSequence {
1845
1890
  getValueAt(beats, usePrev = false) {
1846
1891
  return this.getNodeAt(beats, usePrev).getValueAt(beats);
1847
1892
  }
1893
+ getValueAtBySecs(beats, seconds, timeCalculator, usePrev = false) {
1894
+ return this.getNodeAt(beats, usePrev).getValueAt(seconds, timeCalculator);
1895
+ }
1848
1896
  getFloorPositionAt(beats, timeCalculator) {
1849
1897
  const node = this.getNodeAt(beats);
1850
1898
  const value = node.getLocalFloorPos(beats, timeCalculator) + node.floorPosition;
@@ -1861,7 +1909,7 @@ class EventNodeSequence {
1861
1909
  const prevStart = node.previous.previous;
1862
1910
  currentFP = prevStart.floorPosition + prevStart.getFullLocalFloorPos(tc);
1863
1911
  } else {
1864
- currentFP = 0;
1912
+ node.floorPosition = currentFP = 0;
1865
1913
  }
1866
1914
  while (true) {
1867
1915
  const canBeEnd = node.next;
@@ -1969,35 +2017,69 @@ class EventNodeSequence {
1969
2017
  return;
1970
2018
  }
1971
2019
  let lastEnd = endNode;
2020
+ currentNode = endNode.next;
1972
2021
  while (true) {
2022
+ const evaluator = currentNode.evaluator;
2023
+ if (this.type === 5 /* easing */ && evaluator instanceof EasedEvaluator && evaluator.easing instanceof TemplateEasing) {
2024
+ if (TemplateEasing.checkCircularReference(this, evaluator.easing)) {
2025
+ err.TEMPLATE_EASING_CIRCULAR_REFERENCE(this.id).warn();
2026
+ }
2027
+ }
2028
+ if (evaluator instanceof EasedEvaluator && evaluator.easing instanceof SegmentedEasing) {
2029
+ const easing = evaluator.easing;
2030
+ const inner = easing.easing;
2031
+ if (inner.getValue(easing.left) === inner.getValue(easing.right)) {
2032
+ err.EASING_DELTA_CANNOT_BE_ZERO(this.id, currentNode.time).warn();
2033
+ }
2034
+ }
1973
2035
  const endNode2 = currentNode.next;
1974
2036
  if (endNode2.type === 1 /* TAIL */) {
1975
2037
  break;
1976
2038
  }
2039
+ if (!TC2.gt(endNode2.time, currentNode.time)) {
2040
+ err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${this.id}, ${currentNode.time}`).warn();
2041
+ }
1977
2042
  if (TC2.ne(lastEnd.time, currentNode.time)) {
1978
2043
  err.EVENT_NODE_NOT_DENSE(`${this.id}, ${currentNode.time}`).warn();
1979
2044
  }
1980
2045
  currentNode = currentNode.next.next;
2046
+ lastEnd = endNode2;
2047
+ }
2048
+ }
2049
+ hasReferenceTo(seq) {
2050
+ let node = this.head.next;
2051
+ while (true) {
2052
+ const endNode = node.next;
2053
+ if (endNode.type === 1 /* TAIL */) {
2054
+ break;
2055
+ }
2056
+ const evaluator = node.evaluator;
2057
+ if (evaluator instanceof EasedEvaluator && evaluator.easing instanceof TemplateEasing) {
2058
+ if (TemplateEasing.checkCircularReference(seq, evaluator.easing)) {
2059
+ return true;
2060
+ }
2061
+ }
2062
+ node = endNode.next;
1981
2063
  }
1982
2064
  }
1983
2065
  }
1984
2066
  // src/note.ts
1985
2067
  var notePropTypes = {
1986
2068
  above: "boolean",
1987
- alpha: "number",
2069
+ alpha: "int[0,255]",
1988
2070
  endTime: ["number", "number", "number"],
1989
2071
  isFake: "boolean",
1990
2072
  positionX: "number",
1991
- size: "number",
2073
+ size: "number(0,+)",
1992
2074
  speed: "number",
1993
2075
  startTime: ["number", "number", "number"],
1994
- type: "number",
1995
- visibleTime: "number",
1996
- visibleBeats: "number",
2076
+ type: "int[1,4]",
2077
+ visibleTime: "number(0,+)",
2078
+ visibleBeats: "number(0,+)",
1997
2079
  yOffset: "number",
1998
- tint: ["number", "number", "number"],
1999
- tintHitEffects: ["number", "number", "number"],
2000
- judgeSize: "number"
2080
+ tint: ["int[0,255]", "int[0,255]", "int[0,255]"],
2081
+ tintHitEffects: ["int[0,255]", "int[0,255]", "int[0,255]"],
2082
+ judgeSize: "number(0,+)"
2001
2083
  };
2002
2084
 
2003
2085
  class Note {
@@ -2030,9 +2112,10 @@ class Note {
2030
2112
  this.visibleTime = data.visibleTime;
2031
2113
  this.yOffset = data.absoluteYOffset ?? data.yOffset * this.speed;
2032
2114
  this.visibleBeats = data.visibleBeats;
2033
- this.tint = data.tint ? rgb2hex(data.tint) : undefined;
2115
+ const color = data.tint ?? data.color;
2116
+ this.tint = color ? rgb2hex(color) : undefined;
2034
2117
  this.tintHitEffects = data.tintHitEffects ? rgb2hex(data.tintHitEffects) : undefined;
2035
- this.judgeSize = data.judgeSize ?? this.size;
2118
+ this.judgeSize = data.judgeSize ?? data.judgeArea ?? this.size;
2036
2119
  }
2037
2120
  static fromKPAJSON(data, timeCalculator) {
2038
2121
  const note = new Note(data);
@@ -2078,8 +2161,9 @@ class Note {
2078
2161
  visibleTime,
2079
2162
  yOffset: this.yOffset / this.speed,
2080
2163
  speed: this.speed,
2081
- tint: this.tint !== undefined ? hex2rgb(this.tint) : undefined,
2082
- tintHitEffects: this.tint !== undefined ? hex2rgb(this.tintHitEffects) : undefined
2164
+ tint: this.tint !== undefined && this.tint !== 16777215 ? hex2rgb(this.tint) : undefined,
2165
+ tintHitEffects: this.tintHitEffects !== undefined && this.tintHitEffects !== 16777215 ? hex2rgb(this.tintHitEffects) : undefined,
2166
+ judgeArea: this.judgeSize
2083
2167
  };
2084
2168
  }
2085
2169
  dumpKPA() {
@@ -2096,8 +2180,8 @@ class Note {
2096
2180
  yOffset: this.yOffset / this.speed,
2097
2181
  absoluteYOffset: this.yOffset,
2098
2182
  speed: this.speed,
2099
- tint: this.tint !== undefined ? hex2rgb(this.tint) : undefined,
2100
- tintHitEffects: this.tint !== undefined ? hex2rgb(this.tintHitEffects) : undefined,
2183
+ tint: this.tint !== undefined && this.tint !== 16777215 ? hex2rgb(this.tint) : undefined,
2184
+ tintHitEffects: this.tintHitEffects !== undefined && this.tintHitEffects !== 16777215 ? hex2rgb(this.tintHitEffects) : undefined,
2101
2185
  judgeSize: this.judgeSize && this.judgeSize !== 1 ? this.judgeSize : undefined
2102
2186
  };
2103
2187
  }
@@ -2636,17 +2720,17 @@ class JudgeLine {
2636
2720
  line.speedSequence = speedSequences[0];
2637
2721
  chart.registerEventNodeSequence(4 /* speed */, `#${id}.speed`, speedSequences[0]);
2638
2722
  }
2723
+ if (data.extended?.scaleXEvents) {
2724
+ line.extendedLayer.scaleX = createExtendedSequence(7 /* scaleX */, data.extended.scaleXEvents);
2725
+ } else {
2726
+ line.extendedLayer.scaleX = chart.createEventNodeSequence(7 /* scaleX */, `#${id}.ex.scaleX`);
2727
+ }
2728
+ if (data.extended?.scaleYEvents) {
2729
+ line.extendedLayer.scaleY = createExtendedSequence(8 /* scaleY */, data.extended.scaleYEvents);
2730
+ } else {
2731
+ line.extendedLayer.scaleY = chart.createEventNodeSequence(8 /* scaleY */, `#${id}.ex.scaleY`);
2732
+ }
2639
2733
  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
2734
  if (data.extended.textEvents) {
2651
2735
  line.extendedLayer.text = createExtendedSequence(9 /* text */, data.extended.textEvents);
2652
2736
  }
@@ -2815,7 +2899,7 @@ class JudgeLine {
2815
2899
  if (lineMonotonicity === 0 /* increasing */ && startY >= 0 && endY > 0) {
2816
2900
  return result;
2817
2901
  }
2818
- const computeTime = (speed, currentPos, fore) => timeCalculator.secondsToBeats(currentPos / (speed * 120) + timeCalculator.toSeconds(fore));
2902
+ const computeTime = (speed, currentPos, fore) => speed * currentPos <= 0 ? fore : timeCalculator.secondsToBeats(currentPos / (speed * 120) + timeCalculator.toSeconds(fore));
2819
2903
  while (true) {
2820
2904
  const thisTime = TC2.toBeats(startNode.time);
2821
2905
  const endNode = startNode.next;
@@ -2823,17 +2907,21 @@ class JudgeLine {
2823
2907
  if (endNode.type === 1 /* TAIL */) {
2824
2908
  const thisPosY2 = startNode.floorPosition - currentJudgeLineFloorPos;
2825
2909
  const thisSpeed2 = startNode.value;
2826
- const inf = thisSpeed2 > 0 ? Infinity : thisSpeed2 < 0 ? -Infinity : thisPosY2;
2827
2910
  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;
2911
+ if (thisSpeed2 > 0 && endY > thisPosY2) {
2912
+ range[0] = computeTime(thisSpeed2, startY - thisPosY2, thisTime);
2913
+ } else if (thisSpeed2 < 0 && startY < thisPosY2) {
2914
+ range[0] = computeTime(thisSpeed2, endY - thisPosY2, thisTime);
2915
+ } else if (thisSpeed2 === 0 && startY <= thisPosY2 && thisPosY2 <= endY) {
2916
+ range[0] = thisTime;
2832
2917
  }
2833
2918
  }
2834
2919
  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);
2920
+ if (thisSpeed2 > 0 && endY > thisPosY2) {
2921
+ range[1] = computeTime(thisSpeed2, endY - thisPosY2, thisTime);
2922
+ result.push(range);
2923
+ } else if (thisSpeed2 < 0 && startY < thisPosY2) {
2924
+ range[1] = computeTime(thisSpeed2, startY - thisPosY2, thisTime);
2837
2925
  result.push(range);
2838
2926
  } else if (thisSpeed2 === 0) {
2839
2927
  range[1] = Infinity;
@@ -2846,7 +2934,7 @@ class JudgeLine {
2846
2934
  const thisPosY = startNode.floorPosition - currentJudgeLineFloorPos;
2847
2935
  const nextPosY = nextStart.floorPosition - currentJudgeLineFloorPos;
2848
2936
  let thisSpeed = startNode.value;
2849
- let nextSpeed = nextStart.value;
2937
+ let nextSpeed = endNode.value;
2850
2938
  if (Math.abs(thisSpeed) < 0.00000001) {
2851
2939
  thisSpeed = 0;
2852
2940
  }
@@ -2858,15 +2946,13 @@ class JudgeLine {
2858
2946
  const zeroPosY = speedSequence.getFloorPositionAt(zeroTime, timeCalculator) - currentJudgeLineFloorPos;
2859
2947
  const zeroSpeed = 0;
2860
2948
  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) {
2949
+ if (thisSpeed > 0 && endY >= thisPosY || thisSpeed < 0 && startY <= thisPosY) {
2864
2950
  range[0] = thisTime;
2865
2951
  }
2866
2952
  }
2867
2953
  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);
2954
+ if (thisSpeed > 0 && endY <= zeroPosY || thisSpeed < 0 && startY >= zeroPosY) {
2955
+ range[1] = zeroTime;
2870
2956
  result.push(range);
2871
2957
  if (lineMonotonicity !== 2 /* swinging */) {
2872
2958
  return result;
@@ -2875,15 +2961,13 @@ class JudgeLine {
2875
2961
  }
2876
2962
  }
2877
2963
  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) {
2964
+ if (nextSpeed > 0 && endY >= zeroPosY || nextSpeed < 0 && startY <= zeroPosY) {
2881
2965
  range[0] = zeroTime;
2882
2966
  }
2883
2967
  }
2884
2968
  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);
2969
+ if (nextSpeed > 0 && endY <= nextPosY || nextSpeed < 0 && startY >= nextPosY) {
2970
+ range[1] = nextTime;
2887
2971
  result.push(range);
2888
2972
  if (lineMonotonicity !== 2 /* swinging */) {
2889
2973
  return result;
@@ -2893,15 +2977,24 @@ class JudgeLine {
2893
2977
  }
2894
2978
  } else {
2895
2979
  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) {
2980
+ if (thisSpeed > 0 && endY >= thisPosY && startY < nextPosY) {
2981
+ range[0] = thisSpeed !== nextSpeed ? thisTime : computeTime(thisSpeed, startY - thisPosY, thisTime);
2982
+ } else if (thisSpeed < 0 && startY <= thisPosY && endY > nextPosY) {
2983
+ range[0] = thisSpeed !== nextSpeed ? thisTime : computeTime(thisSpeed, endY - thisPosY, thisTime);
2984
+ } else if (thisSpeed === 0 && startY <= thisPosY && thisPosY <= endY) {
2899
2985
  range[0] = thisTime;
2900
2986
  }
2901
2987
  }
2902
2988
  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);
2989
+ if (thisSpeed > 0 && endY <= nextPosY) {
2990
+ range[1] = thisSpeed !== nextSpeed ? nextTime : computeTime(thisSpeed, endY - thisPosY, thisTime);
2991
+ result.push(range);
2992
+ if (lineMonotonicity !== 2 /* swinging */) {
2993
+ return result;
2994
+ }
2995
+ range = [undefined, undefined];
2996
+ } else if (thisSpeed < 0 && startY >= thisPosY) {
2997
+ range[1] = thisSpeed !== nextSpeed ? nextTime : computeTime(thisSpeed, startY - thisPosY, thisTime);
2905
2998
  result.push(range);
2906
2999
  if (lineMonotonicity !== 2 /* swinging */) {
2907
3000
  return result;
@@ -2934,6 +3027,18 @@ class JudgeLine {
2934
3027
  }
2935
3028
  return current;
2936
3029
  }
3030
+ getStackedValueBySeconds(type, beats, seconds, timeCalculator, usePrev = false) {
3031
+ const length = this.eventLayers.length;
3032
+ let current = 0;
3033
+ for (let index = 0;index < length; index++) {
3034
+ const layer = this.eventLayers[index];
3035
+ if (!layer || !layer[type]) {
3036
+ break;
3037
+ }
3038
+ current += layer[type].getValueAtBySecs(beats, seconds, timeCalculator, usePrev);
3039
+ }
3040
+ return current;
3041
+ }
2937
3042
  getNNList(speed, yOffset, isHold, initsJump) {
2938
3043
  const lists = isHold ? this.hnLists : this.nnLists;
2939
3044
  const medianYOffset = getRangeMedian(yOffset);
@@ -3190,6 +3295,9 @@ class TimeCalculator {
3190
3295
  duration;
3191
3296
  constructor() {}
3192
3297
  initSequence() {
3298
+ if (!this.bpmList || !this.duration) {
3299
+ throw new Error("TimeCalculator: bpmList and duration must be set before initSequence");
3300
+ }
3193
3301
  const bpmList = this.bpmList;
3194
3302
  this.bpmSequence = new BPMSequence(bpmList, this.duration);
3195
3303
  }
@@ -3211,7 +3319,7 @@ class TimeCalculator {
3211
3319
  }
3212
3320
  }
3213
3321
  // src/version.ts
3214
- var VERSION = 212;
3322
+ var VERSION = 213;
3215
3323
  var SCHEMA = "https://cdn.jsdelivr.net/npm/kipphi@2.1.0/chartType2.schema.json";
3216
3324
 
3217
3325
  // src/chart.ts
@@ -3243,6 +3351,7 @@ class Chart {
3243
3351
  scoreAttach = null;
3244
3352
  nameAttach = null;
3245
3353
  levelAttach = null;
3354
+ segmentedTemplates = new Map;
3246
3355
  constructor() {}
3247
3356
  getEffectiveBeats() {
3248
3357
  const effectiveBeats = this.timeCalculator.secondsToBeats(this.duration);
@@ -3251,7 +3360,7 @@ class Chart {
3251
3360
  }
3252
3361
  static fromRPEJSON(data, duration) {
3253
3362
  const chart = new Chart;
3254
- chart.judgeLineGroups = data.judgeLineGroup.map((group) => new JudgeLineGroup(group));
3363
+ chart.judgeLineGroups = (data.judgeLineGroup || ["Default"]).map((group) => new JudgeLineGroup(group));
3255
3364
  chart.name = data.META.name;
3256
3365
  chart.level = data.META.level;
3257
3366
  chart.offset = data.META.offset;
@@ -3334,6 +3443,9 @@ class Chart {
3334
3443
  for (let i = 0;i < len; i++) {
3335
3444
  const easingData = templateEasings[i];
3336
3445
  const sequence = chart.sequenceMap.get(easingData.content);
3446
+ if (!sequence) {
3447
+ continue;
3448
+ }
3337
3449
  if (sequence.type !== 5 /* easing */) {
3338
3450
  throw err.CANNOT_IMPLEMENT_TEMEAS_WITH_NON_EASING_ENS(easingData.name);
3339
3451
  }
@@ -3342,7 +3454,15 @@ class Chart {
3342
3454
  }
3343
3455
  chart.templateEasingLib.implement(easingData.name, sequence);
3344
3456
  }
3457
+ for (let i = 0;i < len; i++) {
3458
+ const easingData = templateEasings[i];
3459
+ const sequence = chart.sequenceMap.get(easingData.content);
3460
+ if (sequence.hasReferenceTo(sequence)) {
3461
+ throw err.TEMPLATE_EASING_CIRCULAR_REFERENCE(easingData.name);
3462
+ }
3463
+ }
3345
3464
  chart.templateEasingLib.check();
3465
+ chart.checkSegmentedTemplates();
3346
3466
  for (const lineData of data.orphanLines) {
3347
3467
  const line = JudgeLine.fromKPAJSON(data.version, chart, lineData.id, lineData, chart.templateEasingLib, chart.timeCalculator);
3348
3468
  chart.orphanLines.push(line);
@@ -3638,6 +3758,21 @@ class Chart {
3638
3758
  }
3639
3759
  return null;
3640
3760
  }
3761
+ checkErrors() {
3762
+ KPAError.flush();
3763
+ for (const [_, seq] of this.sequenceMap) {
3764
+ seq.checkErrors();
3765
+ }
3766
+ }
3767
+ checkSegmentedTemplates() {
3768
+ for (const [easing, [pos, time]] of this.segmentedTemplates) {
3769
+ const inner = easing.easing;
3770
+ if (inner.getValue(easing.left) === inner.getValue(easing.right)) {
3771
+ err.EASING_DELTA_CANNOT_BE_ZERO(pos, time).warn();
3772
+ }
3773
+ }
3774
+ this.segmentedTemplates.clear();
3775
+ }
3641
3776
  }
3642
3777
 
3643
3778
  class JudgeLineGroup {
@@ -4156,6 +4291,13 @@ class EventNodeEvaluatorChangeOperation extends Operation {
4156
4291
  this.node = node;
4157
4292
  this.value = value;
4158
4293
  this.originalValue = this.node.evaluator;
4294
+ const seq = node.parentSeq;
4295
+ if (seq.type === 5 /* easing */ && value instanceof EasedEvaluator && value.easing instanceof TemplateEasing) {
4296
+ const circular = TemplateEasing.checkCircularReference(seq, value.easing);
4297
+ if (circular) {
4298
+ throw err.TEMPLATE_EASING_CIRCULAR_REFERENCE(value.easing.name);
4299
+ }
4300
+ }
4159
4301
  }
4160
4302
  do() {
4161
4303
  this.node.evaluator = this.value;
@@ -5194,14 +5336,34 @@ class RPEChartCompiler {
5194
5336
  chart;
5195
5337
  sequenceMap = new Map;
5196
5338
  interpolationStep = [0, 1, 16];
5339
+ deletesEmptyLines = true;
5197
5340
  constructor(chart2) {
5198
5341
  this.chart = chart2;
5199
5342
  }
5200
5343
  compileChart() {
5201
- console.time("compileChart");
5202
5344
  const chart2 = this.chart;
5203
5345
  const judgeLineGroups = chart2.judgeLineGroups.map((group) => group.name);
5204
- const judgeLineList = chart2.judgeLines.map((line2) => this.compileJudgeLine(line2));
5346
+ const filter = this.deletesEmptyLines ? (line2) => {
5347
+ return line2.nnLists.size > 0 || line2.hnLists.size > 0 || line2.eventLayers.length > 0 || ["moveX", "moveY", "rotate", "alpha"].some((evType) => {
5348
+ const seq = line2.eventLayers[0][evType];
5349
+ let node = seq.head.next;
5350
+ for (let i = 0;i < 2; i++) {
5351
+ const endNode = node.next;
5352
+ if (node.value !== 0) {
5353
+ return true;
5354
+ }
5355
+ if (endNode.type === 1 /* TAIL */) {
5356
+ return false;
5357
+ }
5358
+ if (endNode.value !== 0) {
5359
+ return true;
5360
+ }
5361
+ node = endNode.next;
5362
+ }
5363
+ return true;
5364
+ });
5365
+ } : () => true;
5366
+ const judgeLineList = chart2.judgeLines.filter(filter).map((line2) => this.compileJudgeLine(line2));
5205
5367
  const BPMList = chart2.timeCalculator.dump();
5206
5368
  const META = {
5207
5369
  RPEVersion: 1,
@@ -5240,7 +5402,6 @@ class RPEChartCompiler {
5240
5402
  lineData.attachUI = uiName;
5241
5403
  }
5242
5404
  }
5243
- console.timeEnd("compileChart");
5244
5405
  return {
5245
5406
  BPMList,
5246
5407
  META,
@@ -5248,8 +5409,8 @@ class RPEChartCompiler {
5248
5409
  judgeLineGroup: judgeLineGroups,
5249
5410
  multiLineString: "",
5250
5411
  multiScale: 1,
5251
- chartTime: chart2.rpeChartingTime * 60,
5252
- kpaChartTime: chart2.chartingTime
5412
+ chartTime: chart2.rpeChartingSeconds,
5413
+ kpaChartTime: chart2.chartingSeconds
5253
5414
  };
5254
5415
  }
5255
5416
  compileJudgeLine(judgeLine) {
@@ -5296,7 +5457,7 @@ class RPEChartCompiler {
5296
5457
  bezierPoints: innerEasing instanceof BezierEasing ? [innerEasing.cp1[0], innerEasing.cp1[1], innerEasing.cp2[0], innerEasing.cp2[1]] : [0, 0, 0, 0],
5297
5458
  easingLeft: isSegmented ? easing.left : 0,
5298
5459
  easingRight: isSegmented ? easing.right : 1,
5299
- easingType: easing instanceof NormalEasing ? easing.rpeId ?? 1 : null,
5460
+ easingType: isSegmented ? easing.easing instanceof NormalEasing ? easing.easing.rpeId ?? 1 : null : easing instanceof NormalEasing ? easing.rpeId ?? 1 : null,
5300
5461
  end,
5301
5462
  endTime: endNode.time,
5302
5463
  linkgroup: 0,