kipphi 2.1.3-beta.1 → 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/chart.ts CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  NormalEasing,
11
11
  rpeEasingArray,
12
12
  SegmentedEasing,
13
+ TemplateEasing,
13
14
  TemplateEasingLib,
14
15
  } from "./easing";
15
16
 
@@ -149,6 +150,11 @@ export class Chart {
149
150
  /** 难度等级显示绑定的判定线 */
150
151
  levelAttach: JudgeLine | null = null;
151
152
 
153
+ /** 仅用于构造时检查
154
+ * @internal
155
+ */
156
+ segmentedTemplates: Map<(SegmentedEasing & { easing: TemplateEasing}), [string, TimeT]> = new Map();
157
+
152
158
  constructor() {}
153
159
 
154
160
  /**
@@ -286,6 +292,9 @@ export class Chart {
286
292
  for (let i = 0; i < len; i++) {
287
293
  const easingData = templateEasings[i];
288
294
  const sequence = chart.sequenceMap.get(easingData.content);
295
+ if (!sequence) {
296
+ continue; // 后面check的时候会错误处理
297
+ }
289
298
  if (sequence.type !== EventType.easing) {
290
299
  throw err.CANNOT_IMPLEMENT_TEMEAS_WITH_NON_EASING_ENS(easingData.name);
291
300
  }
@@ -303,7 +312,10 @@ export class Chart {
303
312
  }
304
313
  }
305
314
 
306
- chart.templateEasingLib.check()
315
+ chart.templateEasingLib.check();
316
+
317
+ chart.checkSegmentedTemplates();
318
+
307
319
  for (const lineData of data.orphanLines) {
308
320
  const line: JudgeLine = JudgeLine.fromKPAJSON(data.version, chart, lineData.id, lineData, chart.templateEasingLib, chart.timeCalculator)
309
321
  chart.orphanLines.push(line)
@@ -727,6 +739,25 @@ export class Chart {
727
739
 
728
740
  }
729
741
  */
742
+ checkErrors() {
743
+ KPAError.flush();
744
+ for (const [_, seq] of this.sequenceMap) {
745
+ seq.checkErrors();
746
+ }
747
+ }
748
+ /**
749
+ * 用于构造谱面时检查
750
+ */
751
+ protected checkSegmentedTemplates() {
752
+ for (const [easing, [pos, time]] of this.segmentedTemplates) {
753
+ const inner = easing.easing;
754
+ if (inner.getValue(easing.left) === inner.getValue(easing.right)) {
755
+ err.EASING_DELTA_CANNOT_BE_ZERO(pos, time).warn();
756
+ }
757
+ }
758
+ this.segmentedTemplates.clear();
759
+ // 查完就清除,不再需要
760
+ }
730
761
  }
731
762
 
732
763
  /**
package/env.ts CHANGED
@@ -74,6 +74,7 @@ export enum ERROR_IDS {
74
74
  NODES_NOT_BELONG_TO_SAME_SEQUENCE = EASING | INVALID_USAGE | 2,
75
75
  NODES_HAS_ZERO_DELTA = EASING | INVALID_USAGE | 3,
76
76
  TEMPLATE_EASING_CIRCULAR_REFERENCE = EASING | INVALID_USAGE | 4,
77
+ EASING_DELTA_CANNOT_BE_ZERO = EASING | INVALID_USAGE | 5,
77
78
 
78
79
  CANNOT_DIVIDE_EXPRESSION_EVALUATOR = EVALUATOR | INVALID_USAGE | 0,
79
80
  MISSING_MACRO_EVALUATOR_KEY = EVALUATOR | INVALID_DATA | 0,
@@ -184,6 +185,8 @@ export const ERRORS = {
184
185
  `Hold should have a duration.`,
185
186
  TEMPLATE_EASING_CIRCULAR_REFERENCE: (temEasName: string) =>
186
187
  `Template Easing '${temEasName}' has circular reference`,
188
+ EASING_DELTA_CANNOT_BE_ZERO: (seqName: string, time: TimeT) =>
189
+ `Easing delta cannot be zero. (at ${seqName}, ${toTimeString(time)}`,
187
190
  } satisfies Record<keyof typeof ERROR_IDS, (...args: any[]) => string>
188
191
 
189
192
  type EnumKeys<E extends Record<string, string | number>> = E[keyof E];
package/event.ts CHANGED
@@ -640,6 +640,12 @@ export class EventNodeSequence<VT extends EventValueESType = number> { // 泛型
640
640
  for (let index = 0; index < length; index++) {
641
641
  const event = data[index];
642
642
  const [start, end] = chart.createEventFromData<VT>(event, valueType, `${pos}.events[${index}]`);
643
+
644
+ // 收集被截的模板缓动
645
+ const evaluator = start.evaluator;
646
+ if (evaluator instanceof EasedEvaluator && evaluator.easing instanceof SegmentedEasing && evaluator.easing.easing instanceof TemplateEasing) {
647
+ chart.segmentedTemplates.set(evaluator.easing as any, [pos, start.time]);
648
+ }
643
649
  // 从前面复制了,复用性减一
644
650
  // KPA2没有更改RPE的按事件存储的机制。
645
651
  if (TC.lt(event.startTime, lastEndTime)) { // event.startTime < lastEndTime
@@ -956,15 +962,33 @@ export class EventNodeSequence<VT extends EventValueESType = number> { // 泛型
956
962
  }
957
963
 
958
964
  let lastEnd: EventEndNode<VT> = endNode;
965
+ currentNode = endNode.next;
959
966
  while (true) {
967
+ const evaluator = currentNode.evaluator;
968
+ if (this.type === EventType.easing && evaluator instanceof EasedEvaluator && evaluator.easing instanceof TemplateEasing) {
969
+ if (TemplateEasing.checkCircularReference(this as EventNodeSequence, evaluator.easing)) {
970
+ err.TEMPLATE_EASING_CIRCULAR_REFERENCE(this.id).warn();
971
+ }
972
+ }
973
+ if (evaluator instanceof EasedEvaluator && evaluator.easing instanceof SegmentedEasing) {
974
+ const easing = evaluator.easing;
975
+ const inner = easing.easing;
976
+ if (inner.getValue(easing.left) === inner.getValue(easing.right)) {
977
+ err.EASING_DELTA_CANNOT_BE_ZERO(this.id, currentNode.time).warn();
978
+ }
979
+ }
960
980
  const endNode = currentNode.next;
961
981
  if (endNode.type === NodeType.TAIL) {
962
982
  break;
963
983
  }
984
+ if (!TC.gt(endNode.time, currentNode.time)) {
985
+ err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${this.id}, ${currentNode.time}`).warn();
986
+ }
964
987
  if (TC.ne(lastEnd.time, currentNode.time)) {
965
988
  err.EVENT_NODE_NOT_DENSE(`${this.id}, ${currentNode.time}`).warn();
966
989
  }
967
990
  currentNode = currentNode.next.next;
991
+ lastEnd = endNode;
968
992
  }
969
993
  }
970
994
  hasReferenceTo(this: EventNodeSequence<number>, seq: EventNodeSequence<number>) {
package/index.d.ts CHANGED
@@ -1697,7 +1697,7 @@ declare module "easing" {
1697
1697
  }
1698
1698
  declare module "chart" {
1699
1699
  import { TimeCalculator } from "bpm";
1700
- import { Easing, TemplateEasingLib } from "easing";
1700
+ import { Easing, SegmentedEasing, TemplateEasing, TemplateEasingLib } from "easing";
1701
1701
  import { EventNodeSequence, EventStartNode, EventEndNode, EventNode } from "event";
1702
1702
  import { JudgeLine } from "judgeline";
1703
1703
  import { NNNList, NNNode } from "note";
@@ -1765,6 +1765,12 @@ declare module "chart" {
1765
1765
  nameAttach: JudgeLine | null;
1766
1766
  /** 难度等级显示绑定的判定线 */
1767
1767
  levelAttach: JudgeLine | null;
1768
+ /** 仅用于构造时检查
1769
+ * @internal
1770
+ */
1771
+ segmentedTemplates: Map<(SegmentedEasing & {
1772
+ easing: TemplateEasing;
1773
+ }), [string, TimeT]>;
1768
1774
  constructor();
1769
1775
  /**
1770
1776
  * 获取有效节拍数
@@ -1898,6 +1904,11 @@ declare module "chart" {
1898
1904
  bindTimeMacro(node: EventStartNode<any>, macroData: MacroData, pos: string): any;
1899
1905
  bindValueMacro(node: EventNode<any>, macroData: MacroData, pos: string): any;
1900
1906
  linkMacro(node: EventNode<EventValueESType>, linkData: MacroLink, pos: string): any;
1907
+ checkErrors(): void;
1908
+ /**
1909
+ * 用于构造谱面时检查
1910
+ */
1911
+ protected checkSegmentedTemplates(): void;
1901
1912
  }
1902
1913
  /**
1903
1914
  * 表示一组判定线的容器
@@ -2262,6 +2273,7 @@ declare module "env" {
2262
2273
  NODES_NOT_BELONG_TO_SAME_SEQUENCE = 2610,
2263
2274
  NODES_HAS_ZERO_DELTA = 2611,
2264
2275
  TEMPLATE_EASING_CIRCULAR_REFERENCE = 2612,
2276
+ EASING_DELTA_CANNOT_BE_ZERO = 2613,
2265
2277
  CANNOT_DIVIDE_EXPRESSION_EVALUATOR = 2352,
2266
2278
  MISSING_MACRO_EVALUATOR_KEY = 2336,
2267
2279
  MACRO_EVALUATOR_NOT_FOUND = 2337,
@@ -2313,6 +2325,7 @@ declare module "env" {
2313
2325
  EVENT_NODE_NOT_DENSE: (pos: string) => string;
2314
2326
  HOLD_HAS_NO_DURATION: () => string;
2315
2327
  TEMPLATE_EASING_CIRCULAR_REFERENCE: (temEasName: string) => string;
2328
+ EASING_DELTA_CANNOT_BE_ZERO: (seqName: string, time: TimeT) => string;
2316
2329
  };
2317
2330
  interface ErrorMap extends Record<keyof typeof ERROR_IDS, Array<any>> {
2318
2331
  EVENT_NODE_NOT_DENSE: [EventNode];
@@ -2379,6 +2392,7 @@ declare module "env" {
2379
2392
  EVENT_NODE_NOT_DENSE: (pos: string) => KPAError<ERROR_IDS.EVENT_NODE_NOT_DENSE>;
2380
2393
  HOLD_HAS_NO_DURATION: () => KPAError<ERROR_IDS.HOLD_HAS_NO_DURATION>;
2381
2394
  TEMPLATE_EASING_CIRCULAR_REFERENCE: (temEasName: string) => KPAError<ERROR_IDS.TEMPLATE_EASING_CIRCULAR_REFERENCE>;
2395
+ EASING_DELTA_CANNOT_BE_ZERO: (seqName: string, time: TimeT) => KPAError<ERROR_IDS.EASING_DELTA_CANNOT_BE_ZERO>;
2382
2396
  };
2383
2397
  freeze(): void;
2384
2398
  };
package/index.js CHANGED
@@ -79,6 +79,7 @@ var ERROR_IDS;
79
79
  ERROR_IDS2[ERROR_IDS2["NODES_NOT_BELONG_TO_SAME_SEQUENCE"] = EASING | INVALID_USAGE | 2] = "NODES_NOT_BELONG_TO_SAME_SEQUENCE";
80
80
  ERROR_IDS2[ERROR_IDS2["NODES_HAS_ZERO_DELTA"] = EASING | INVALID_USAGE | 3] = "NODES_HAS_ZERO_DELTA";
81
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";
82
83
  ERROR_IDS2[ERROR_IDS2["CANNOT_DIVIDE_EXPRESSION_EVALUATOR"] = EVALUATOR | INVALID_USAGE | 0] = "CANNOT_DIVIDE_EXPRESSION_EVALUATOR";
83
84
  ERROR_IDS2[ERROR_IDS2["MISSING_MACRO_EVALUATOR_KEY"] = EVALUATOR | INVALID_DATA | 0] = "MISSING_MACRO_EVALUATOR_KEY";
84
85
  ERROR_IDS2[ERROR_IDS2["MACRO_EVALUATOR_NOT_FOUND"] = EVALUATOR | INVALID_DATA | 1] = "MACRO_EVALUATOR_NOT_FOUND";
@@ -129,7 +130,8 @@ var ERRORS = {
129
130
  MACRO_NOT_PARAMETRIC: (macroId, pos) => `Macro '${macroId}' is not parametric. At ${pos}`,
130
131
  EVENT_NODE_NOT_DENSE: (pos) => `EventNode is not dense. At ${pos}`,
131
132
  HOLD_HAS_NO_DURATION: () => `Hold should have a duration.`,
132
- TEMPLATE_EASING_CIRCULAR_REFERENCE: (temEasName) => `Template Easing '${temEasName}' has circular reference`
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)}`
133
135
  };
134
136
 
135
137
  class KPAError extends Error {
@@ -1771,6 +1773,10 @@ class EventNodeSequence {
1771
1773
  for (let index = 0;index < length; index++) {
1772
1774
  const event = data[index];
1773
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
+ }
1774
1780
  if (TC2.lt(event.startTime, lastEndTime)) {
1775
1781
  err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}] and the previous`).warn();
1776
1782
  }
@@ -2011,15 +2017,33 @@ class EventNodeSequence {
2011
2017
  return;
2012
2018
  }
2013
2019
  let lastEnd = endNode;
2020
+ currentNode = endNode.next;
2014
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
+ }
2015
2035
  const endNode2 = currentNode.next;
2016
2036
  if (endNode2.type === 1 /* TAIL */) {
2017
2037
  break;
2018
2038
  }
2039
+ if (!TC2.gt(endNode2.time, currentNode.time)) {
2040
+ err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${this.id}, ${currentNode.time}`).warn();
2041
+ }
2019
2042
  if (TC2.ne(lastEnd.time, currentNode.time)) {
2020
2043
  err.EVENT_NODE_NOT_DENSE(`${this.id}, ${currentNode.time}`).warn();
2021
2044
  }
2022
2045
  currentNode = currentNode.next.next;
2046
+ lastEnd = endNode2;
2023
2047
  }
2024
2048
  }
2025
2049
  hasReferenceTo(seq) {
@@ -2137,8 +2161,8 @@ class Note {
2137
2161
  visibleTime,
2138
2162
  yOffset: this.yOffset / this.speed,
2139
2163
  speed: this.speed,
2140
- tint: this.tint !== undefined ? hex2rgb(this.tint) : undefined,
2141
- 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,
2142
2166
  judgeArea: this.judgeSize
2143
2167
  };
2144
2168
  }
@@ -2156,8 +2180,8 @@ class Note {
2156
2180
  yOffset: this.yOffset / this.speed,
2157
2181
  absoluteYOffset: this.yOffset,
2158
2182
  speed: this.speed,
2159
- tint: this.tint !== undefined ? hex2rgb(this.tint) : undefined,
2160
- 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,
2161
2185
  judgeSize: this.judgeSize && this.judgeSize !== 1 ? this.judgeSize : undefined
2162
2186
  };
2163
2187
  }
@@ -3327,6 +3351,7 @@ class Chart {
3327
3351
  scoreAttach = null;
3328
3352
  nameAttach = null;
3329
3353
  levelAttach = null;
3354
+ segmentedTemplates = new Map;
3330
3355
  constructor() {}
3331
3356
  getEffectiveBeats() {
3332
3357
  const effectiveBeats = this.timeCalculator.secondsToBeats(this.duration);
@@ -3418,6 +3443,9 @@ class Chart {
3418
3443
  for (let i = 0;i < len; i++) {
3419
3444
  const easingData = templateEasings[i];
3420
3445
  const sequence = chart.sequenceMap.get(easingData.content);
3446
+ if (!sequence) {
3447
+ continue;
3448
+ }
3421
3449
  if (sequence.type !== 5 /* easing */) {
3422
3450
  throw err.CANNOT_IMPLEMENT_TEMEAS_WITH_NON_EASING_ENS(easingData.name);
3423
3451
  }
@@ -3434,6 +3462,7 @@ class Chart {
3434
3462
  }
3435
3463
  }
3436
3464
  chart.templateEasingLib.check();
3465
+ chart.checkSegmentedTemplates();
3437
3466
  for (const lineData of data.orphanLines) {
3438
3467
  const line = JudgeLine.fromKPAJSON(data.version, chart, lineData.id, lineData, chart.templateEasingLib, chart.timeCalculator);
3439
3468
  chart.orphanLines.push(line);
@@ -3729,6 +3758,21 @@ class Chart {
3729
3758
  }
3730
3759
  return null;
3731
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
+ }
3732
3776
  }
3733
3777
 
3734
3778
  class JudgeLineGroup {
package/note.ts CHANGED
@@ -168,8 +168,8 @@ export class Note {
168
168
  visibleTime: visibleTime,
169
169
  yOffset: this.yOffset / this.speed,
170
170
  speed: this.speed,
171
- tint: this.tint !== undefined ? hex2rgb(this.tint) : undefined,
172
- 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
173
  judgeArea: this.judgeSize
174
174
  }
175
175
  }
@@ -189,8 +189,8 @@ export class Note {
189
189
  /** 但是有历史包袱,所以加字段 */
190
190
  absoluteYOffset: this.yOffset,
191
191
  speed: this.speed,
192
- tint: this.tint !== undefined ? hex2rgb(this.tint) : undefined,
193
- 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,
194
194
  judgeSize: this.judgeSize && this.judgeSize !== 1.0 ? this.judgeSize : undefined,
195
195
  }
196
196
  }
package/operation/easy.ts CHANGED
@@ -35,20 +35,21 @@ class Operable {
35
35
 
36
36
 
37
37
  class OperableNote extends Operable {
38
- private _startTime: TimeT;
39
- private _endTime: TimeT;
40
- private _type: NoteType;
38
+ // @ts-expect-error 后面会赋值
39
+ private _fields: {
40
+ [x in NotePropName]: Note[x]
41
+ } = {};
41
42
  constructor(public target: Note, buffer: Operation[]) {
42
43
  super(buffer);
43
44
  if (target.parentNode === null) {
44
45
  throw new Error("Note has no parent node")
45
46
  }
46
- this._startTime = target.startTime;
47
- this._endTime = target.endTime;
48
- this._type = target.type;
47
+ this._fields.startTime = target.startTime;
48
+ this._fields.endTime = target.endTime;
49
+ this._fields.type = target.type;
49
50
  }
50
51
  get startTime() {
51
- return this._startTime;
52
+ return this._fields.startTime;
52
53
  }
53
54
  set startTime(userTime: Time) {
54
55
  const timeT = userTimeToTuple(userTime);
@@ -60,28 +61,28 @@ class OperableNote extends Operable {
60
61
  if (beats > nnList.effectiveBeats) {
61
62
  throw new Error("")
62
63
  }
63
- this._startTime = timeT;
64
+ this._fields.startTime = timeT;
64
65
  const node = nnList.getNodeOf(timeT);
65
66
  this.buffer.push(NoteTimeChangeOperation.lazy(this.target, node));
66
67
  }
67
- get endTime() { return this._endTime; }
68
+ get endTime() { return this._fields.endTime; }
68
69
  set endTime(userTime: Time) {
69
- if (this._type !== NoteType.hold) {
70
+ if (this._fields.type !== NoteType.hold) {
70
71
  throw new Error("Note is not a hold note");
71
72
  }
72
73
  const timeT = userTimeToTuple(userTime);
73
- if (!TC.gt(timeT, this._startTime)) {
74
+ if (!TC.gt(timeT, this._fields.startTime)) {
74
75
  throw new Error("");
75
76
  }
76
- this._endTime = timeT;
77
+ this._fields.endTime = timeT;
77
78
  this.buffer.push(HoldEndTimeChangeOperation.lazy(this.target, timeT));
78
79
  }
79
- get type() { return this._type; }
80
+ get type() { return this._fields.type; }
80
81
  set type(type: NoteType) {
81
- if (this._type === type) {
82
+ if (this._fields.type === type) {
82
83
  return;
83
84
  }
84
- this._type = type;
85
+ this._fields.type = type;
85
86
  this.buffer.push(NoteTypeChangeOperation.lazy(this.target, type));
86
87
  }
87
88
  }
@@ -109,8 +110,12 @@ interface OperableNote {
109
110
 
110
111
  for (const propName of ["above", "alpha", "positionX", "judgeSize", "isFake", "size", "tint", "tintHitEffects", "visibleBeats"] satisfies NotePropName[]) {
111
112
  Object.defineProperty(OperableNote.prototype, propName, {
112
- get() { return this.target[propName]},
113
+ get() { return this._fields[propName] ?? this.target[propName]},
113
114
  set(value) {
115
+ if (this._fields[propName] === value) {
116
+ return;
117
+ }
118
+ this._fields[propName] = value;
114
119
  this.buffer.push(NotePropChangeOperation.lazy(this.target, propName, value));
115
120
  }
116
121
  });
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.3-beta.1",
4
+ "version": "2.1.3-beta.2",
5
5
  "author": "Team Zincs (https://github.com/TeamZincs)",
6
6
  "license": "MIT",
7
7
  "repository": {