kipphi 2.1.1 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bpm.ts CHANGED
@@ -63,6 +63,15 @@ export class BPMEndNode extends EventEndNode {
63
63
  override previous: BPMStartNode;
64
64
  /** 下一个BPM起始节点 */
65
65
  override next: BPMStartNode;
66
+
67
+ // @ts-expect-error 强加一个getter
68
+ get value(): number {
69
+ return this.previous.value;
70
+ }
71
+ set value(value: number) {
72
+ if (!this.previous) { return; }
73
+ this.previous.value = value;
74
+ }
66
75
 
67
76
  /**
68
77
  * 创建一个新的BPM结束节点
package/env.ts CHANGED
@@ -53,6 +53,7 @@ export enum ERROR_IDS {
53
53
  SEQUENCE_NODE_TIME_OCCUPIED = ENS | OCCPIED | 1,
54
54
  INVALID_EVENT_NODE_SEQUENCE_TYPE = ENS | INVALID_DATA | 0,
55
55
  EVENT_NODE_TIME_NOT_INCREMENTAL = ENS | INVALID_DATA | 1,
56
+ EVENT_NODE_NOT_DENSE = ENS | INVALID_DATA | 2,
56
57
  PARENT_SEQUENCE_NOT_FOUND = ENS | INVALID_USAGE | 1,
57
58
  NEEDS_AT_LEAST_ONE_ENS = ENS | INVALID_USAGE | 2,
58
59
  SEQUENCE_TYPE_NOT_CONSISTENT = ENS | INVALID_USAGE | 3,
@@ -80,6 +81,7 @@ export enum ERROR_IDS {
80
81
 
81
82
 
82
83
  INVALID_NOTE_PROP_TYPE = NOTE | INVALID_TYPE | 0,
84
+ HOLD_HAS_NO_DURATION = NOTE | INVALID_DATA | 0,
83
85
 
84
86
 
85
87
  INVALID_TIME_TUPLE = TC | INVALID_DATA | 0,
@@ -175,9 +177,39 @@ export const ERRORS = {
175
177
  `Parametric Macro requires key. At ${pos}`,
176
178
  MACRO_NOT_PARAMETRIC: (macroId: string, pos) =>
177
179
  `Macro '${macroId}' is not parametric. At ${pos}`,
180
+ EVENT_NODE_NOT_DENSE: (pos: string) =>
181
+ `EventNode is not dense. At ${pos}`,
182
+ HOLD_HAS_NO_DURATION: () =>
183
+ `Hold should have a duration.`,
178
184
  } satisfies Record<keyof typeof ERROR_IDS, (...args: any[]) => string>
179
185
 
186
+ type EnumKeys<E extends Record<string, string | number>> = E[keyof E];
187
+
188
+ // 工具类型:检查 T 的键是否恰好等于枚举的值集合
189
+ type AssertExactEnumKeys<T, E extends Record<string, string | number>> =
190
+ // 1. 收集 T 的键(作为字符串字面量类型)
191
+ (keyof T) extends (keyof E) // 不能有多余键
192
+ ? (keyof E) extends (keyof T) // 不能缺键
193
+ ? T // 通过检查
194
+ : { error: any/*error: "Interface is missing some enum keys"*/ }
195
+ : { error: "Interface has extra keys not in enum" };
196
+
197
+ // 用类型别名做“编译期断言”
198
+ type _CheckMap = AssertExactEnumKeys<ErrorMap, typeof ERROR_IDS>;
199
+ interface _Error extends _CheckMap {
200
+ error: "";
201
+ }
202
+
203
+
204
+ interface ErrorMap extends Record<keyof typeof ERROR_IDS, Array<any>>{
205
+ EVENT_NODE_NOT_DENSE: [EventNode];
206
+ }
207
+
208
+ type ArgsOf<ET extends ERROR_IDS> = ET extends keyof ErrorMap ? ErrorMap[ET] : never;
209
+
180
210
  export class KPAError<ET extends ERROR_IDS> extends Error {
211
+ fixed = false;
212
+ args: ArgsOf<ET> | [];
181
213
  constructor(message: string, public id: ET) {
182
214
  super(message);
183
215
  }
@@ -186,8 +218,15 @@ export class KPAError<ET extends ERROR_IDS> extends Error {
186
218
  *
187
219
  * 此时可以调用该方法,该方法会输出错误并把它保存到KPAError的一个`buffer`静态属性下。
188
220
  */
189
- warn() {
221
+ warn(...args: ArgsOf<ET> | []) {
190
222
  console.warn(this.stack);
223
+ this.args = args;
224
+ KPAError.buffer.push(this);
225
+ }
226
+ fix(...args: ArgsOf<ET> | []) {
227
+ console.warn("[Auto Fixed]" + this.stack);
228
+ this.fixed = true;
229
+ this.args = args;
191
230
  KPAError.buffer.push(this);
192
231
  }
193
232
  static buffer: KPAError<ERROR_IDS>[] = [];
package/event.ts CHANGED
@@ -505,7 +505,7 @@ export class EventNodeSequence<VT extends EventValueESType = number> { // 泛型
505
505
  return type === EventType.speed ? 10 :
506
506
  type === EventType.scaleX || type === EventType.scaleY ? 1.0 :
507
507
  type === EventType.text ? "" :
508
- type === EventType.color ? [0, 0, 0] :
508
+ type === EventType.color ? [255, 255, 255] :
509
509
  0
510
510
  }
511
511
  /**
@@ -649,32 +649,28 @@ export class EventNodeSequence<VT extends EventValueESType = number> { // 泛型
649
649
  err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}] and the previous`).warn()
650
650
  }
651
651
  if (!TC.lt(event.startTime, event.endTime)) {
652
- err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}]`).warn()
652
+ if (TC.eq(event.startTime, event.endTime)) {
653
+ // 零长事件直接忽略,并认为已经修复
654
+ err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}]`).fix();
655
+ continue;
656
+ } else {
657
+ err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}]`).warn()
658
+ }
653
659
  }
654
- lastEndTime = event.endTime;
655
660
  if (lastEnd.type === NodeType.HEAD) {
656
661
  EventNode.connect(lastEnd, start)
657
- // 如果上一个是钩定事件,那么一块捋平
658
- } else if (
659
- lastEnd.value === lastEnd.previous.value
660
- && lastEnd.previous.evaluator instanceof EasedEvaluator
661
- ) {
662
- lastEnd.time = start.time
663
- EventNode.connect(lastEnd, start)
664
- } else if (TC.eq(lastEnd.time, start.time)) {
665
- const val = lastEnd.value;
666
- const midStart = new EventStartNode(lastEnd.time, val);
667
- const midEnd = new EventEndNode(start.time, val);
668
- midStart.evaluator = lastEnd.previous.evaluator;
669
- EventNode.connect(lastEnd, midStart);
670
- EventNode.connect(midStart, midEnd);
662
+ } else if (TC.gt(event.startTime, lastEndTime)) {
663
+ err.EVENT_NODE_NOT_DENSE(`${pos}.events[${index}]`).warn();
664
+ const mid = new EventStartNode(lastEndTime, start.value);
665
+ const midEnd = new EventEndNode(event.startTime, end.value);
666
+ EventNode.connect(lastEnd, mid);
667
+ EventNode.connect(mid, midEnd);
671
668
  EventNode.connect(midEnd, start);
672
- listLength++;
673
669
  } else {
674
-
675
670
  EventNode.connect(lastEnd, start)
676
671
  }
677
672
 
673
+ lastEndTime = event.endTime;
678
674
  lastEnd = end;
679
675
  }
680
676
  const last = lastEnd;
@@ -948,6 +944,29 @@ export class EventNodeSequence<VT extends EventValueESType = number> { // 泛型
948
944
  dest.initJump();
949
945
  return dest;
950
946
  }
947
+ checkErrors() {
948
+ let currentNode: EventStartNode<VT> = this.head.next;
949
+ if (TC.ne(currentNode.time, [0, 0, 1])) {
950
+ err.EVENT_NODE_NOT_DENSE(`${this.id}, ${currentNode.time}`).fix();
951
+ currentNode.time = [0, 0, 1];
952
+ }
953
+ const endNode = currentNode.next;
954
+ if (endNode.type === NodeType.TAIL) {
955
+ return;
956
+ }
957
+
958
+ let lastEnd: EventEndNode<VT> = endNode;
959
+ while (true) {
960
+ const endNode = currentNode.next;
961
+ if (endNode.type === NodeType.TAIL) {
962
+ break;
963
+ }
964
+ if (TC.ne(lastEnd.time, currentNode.time)) {
965
+ err.EVENT_NODE_NOT_DENSE(`${this.id}, ${currentNode.time}`).warn();
966
+ }
967
+ currentNode = currentNode.next.next;
968
+ }
969
+ }
951
970
  }
952
971
 
953
972
  export type SpeedENS = EventNodeSequence<number> & { type: EventType.speed };
package/index.d.ts CHANGED
@@ -533,7 +533,7 @@ declare module "chartTypes" {
533
533
  export type ExtendedEventTypeName = "scaleX" | "scaleY" | "text" | "color";
534
534
  }
535
535
  declare module "version" {
536
- export const VERSION = 211;
536
+ export const VERSION = 212;
537
537
  export const SCHEMA = "https://cdn.jsdelivr.net/npm/kipphi@2.1.0/chartType2.schema.json";
538
538
  }
539
539
  declare module "util" {
@@ -652,6 +652,8 @@ declare module "bpm" {
652
652
  previous: BPMStartNode;
653
653
  /** 下一个BPM起始节点 */
654
654
  next: BPMStartNode;
655
+ get value(): number;
656
+ set value(value: number);
655
657
  /**
656
658
  * 创建一个新的BPM结束节点
657
659
  * @param endTime 节点结束时间
@@ -2179,6 +2181,7 @@ declare module "event" {
2179
2181
  * @throws {KPAError<ERROR_IDS.NEEDS_AT_LEAST_ONE_ENS>}
2180
2182
  */
2181
2183
  static mergeSequences(sequences: EventNodeSequence[]): EventNodeSequence<number>;
2184
+ checkErrors(): void;
2182
2185
  }
2183
2186
  export type SpeedENS = EventNodeSequence<number> & {
2184
2187
  type: EventType.speed;
@@ -2186,12 +2189,14 @@ declare module "event" {
2186
2189
  }
2187
2190
  declare module "env" {
2188
2191
  import { type TimeT, type EventValueType } from "chartTypes";
2192
+ import { EventNode } from "event";
2189
2193
  export enum ERROR_IDS {
2190
2194
  UI_OCCUPIED = 272,
2191
2195
  SEQUENCE_NAME_OCCUPIED = 1552,
2192
2196
  SEQUENCE_NODE_TIME_OCCUPIED = 1553,
2193
2197
  INVALID_EVENT_NODE_SEQUENCE_TYPE = 1568,
2194
2198
  EVENT_NODE_TIME_NOT_INCREMENTAL = 1569,
2199
+ EVENT_NODE_NOT_DENSE = 1570,
2195
2200
  PARENT_SEQUENCE_NOT_FOUND = 1585,
2196
2201
  NEEDS_AT_LEAST_ONE_ENS = 1586,
2197
2202
  SEQUENCE_TYPE_NOT_CONSISTENT = 1587,
@@ -2212,6 +2217,7 @@ declare module "env" {
2212
2217
  MISSING_MACRO_EVALUATOR_KEY = 2336,
2213
2218
  MACRO_EVALUATOR_NOT_FOUND = 2337,
2214
2219
  INVALID_NOTE_PROP_TYPE = 2880,
2220
+ HOLD_HAS_NO_DURATION = 2848,
2215
2221
  INVALID_TIME_TUPLE = 3104,
2216
2222
  TIME_MACRO_NOT_FOUND = 3360,
2217
2223
  VALUE_MACRO_NOT_FOUND = 3361,
@@ -2255,16 +2261,25 @@ declare module "env" {
2255
2261
  JAVASCRIPT_SYNTAX_ERROR: (error: Error, macroId: string) => string;
2256
2262
  PARAMETRIC_MACRO_REQUIRES_PROTO_KEY: (pos: any) => string;
2257
2263
  MACRO_NOT_PARAMETRIC: (macroId: string, pos: any) => string;
2264
+ EVENT_NODE_NOT_DENSE: (pos: string) => string;
2265
+ HOLD_HAS_NO_DURATION: () => string;
2258
2266
  };
2267
+ interface ErrorMap extends Record<keyof typeof ERROR_IDS, Array<any>> {
2268
+ EVENT_NODE_NOT_DENSE: [EventNode];
2269
+ }
2270
+ type ArgsOf<ET extends ERROR_IDS> = ET extends keyof ErrorMap ? ErrorMap[ET] : never;
2259
2271
  export class KPAError<ET extends ERROR_IDS> extends Error {
2260
2272
  id: ET;
2273
+ fixed: boolean;
2274
+ args: ArgsOf<ET> | [];
2261
2275
  constructor(message: string, id: ET);
2262
2276
  /**
2263
2277
  * 对于解析谱面等场景,有时可能需要找出全部的错误,不宜直接抛出错误中断代码执行
2264
2278
  *
2265
2279
  * 此时可以调用该方法,该方法会输出错误并把它保存到KPAError的一个`buffer`静态属性下。
2266
2280
  */
2267
- warn(): void;
2281
+ warn(...args: ArgsOf<ET> | []): void;
2282
+ fix(...args: ArgsOf<ET> | []): void;
2268
2283
  static buffer: KPAError<ERROR_IDS>[];
2269
2284
  static flush(): void;
2270
2285
  }
@@ -2311,6 +2326,8 @@ declare module "env" {
2311
2326
  JAVASCRIPT_SYNTAX_ERROR: (error: Error, macroId: string) => KPAError<ERROR_IDS.JAVASCRIPT_SYNTAX_ERROR>;
2312
2327
  PARAMETRIC_MACRO_REQUIRES_PROTO_KEY: (pos: any) => KPAError<ERROR_IDS.PARAMETRIC_MACRO_REQUIRES_PROTO_KEY>;
2313
2328
  MACRO_NOT_PARAMETRIC: (macroId: string, pos: any) => KPAError<ERROR_IDS.MACRO_NOT_PARAMETRIC>;
2329
+ EVENT_NODE_NOT_DENSE: (pos: string) => KPAError<ERROR_IDS.EVENT_NODE_NOT_DENSE>;
2330
+ HOLD_HAS_NO_DURATION: () => KPAError<ERROR_IDS.HOLD_HAS_NO_DURATION>;
2314
2331
  };
2315
2332
  freeze(): void;
2316
2333
  };
@@ -2423,6 +2440,7 @@ declare module "operation/basic" {
2423
2440
  undo(): void;
2424
2441
  redo(): void;
2425
2442
  do(operation: Operation): void;
2443
+ tryDo(returnOperation: () => Operation): void;
2426
2444
  processFlags(operation: Operation): void;
2427
2445
  clear(): void;
2428
2446
  addEventListener<T extends OpEventType>(type: T, listener: (event: OpEventMap[T]) => void, options?: boolean | AddEventListenerOptions): void;
@@ -2518,8 +2536,8 @@ declare module "operation/event" {
2518
2536
  * @param targetPrevious 要插在谁后面 The node to insert after, accessed through `EventNodeSequence.getNodeAt(TC.toBeats(node))`
2519
2537
  */
2520
2538
  constructor(node: EventStartNode<VT>, targetPrevious: EventStartNode<VT>, updatesFP?: boolean);
2521
- do(): void;
2522
- undo(): void;
2539
+ do(chart: Chart): void;
2540
+ undo(chart: Chart): void;
2523
2541
  }
2524
2542
  export class EventNodePairInsertOrOverwriteOperation<VT extends EventValueESType> extends UnionOperation<LazyOperation<typeof EventNodePairInsertOperation<VT>> | EventNodeValueChangeOperation<VT>> {
2525
2543
  overlapping: boolean;
package/index.js CHANGED
@@ -60,6 +60,7 @@ var ERROR_IDS;
60
60
  ERROR_IDS2[ERROR_IDS2["SEQUENCE_NODE_TIME_OCCUPIED"] = ENS | OCCPIED | 1] = "SEQUENCE_NODE_TIME_OCCUPIED";
61
61
  ERROR_IDS2[ERROR_IDS2["INVALID_EVENT_NODE_SEQUENCE_TYPE"] = ENS | INVALID_DATA | 0] = "INVALID_EVENT_NODE_SEQUENCE_TYPE";
62
62
  ERROR_IDS2[ERROR_IDS2["EVENT_NODE_TIME_NOT_INCREMENTAL"] = ENS | INVALID_DATA | 1] = "EVENT_NODE_TIME_NOT_INCREMENTAL";
63
+ ERROR_IDS2[ERROR_IDS2["EVENT_NODE_NOT_DENSE"] = ENS | INVALID_DATA | 2] = "EVENT_NODE_NOT_DENSE";
63
64
  ERROR_IDS2[ERROR_IDS2["PARENT_SEQUENCE_NOT_FOUND"] = ENS | INVALID_USAGE | 1] = "PARENT_SEQUENCE_NOT_FOUND";
64
65
  ERROR_IDS2[ERROR_IDS2["NEEDS_AT_LEAST_ONE_ENS"] = ENS | INVALID_USAGE | 2] = "NEEDS_AT_LEAST_ONE_ENS";
65
66
  ERROR_IDS2[ERROR_IDS2["SEQUENCE_TYPE_NOT_CONSISTENT"] = ENS | INVALID_USAGE | 3] = "SEQUENCE_TYPE_NOT_CONSISTENT";
@@ -80,6 +81,7 @@ var ERROR_IDS;
80
81
  ERROR_IDS2[ERROR_IDS2["MISSING_MACRO_EVALUATOR_KEY"] = EVALUATOR | INVALID_DATA | 0] = "MISSING_MACRO_EVALUATOR_KEY";
81
82
  ERROR_IDS2[ERROR_IDS2["MACRO_EVALUATOR_NOT_FOUND"] = EVALUATOR | INVALID_DATA | 1] = "MACRO_EVALUATOR_NOT_FOUND";
82
83
  ERROR_IDS2[ERROR_IDS2["INVALID_NOTE_PROP_TYPE"] = NOTE | INVALID_TYPE | 0] = "INVALID_NOTE_PROP_TYPE";
84
+ ERROR_IDS2[ERROR_IDS2["HOLD_HAS_NO_DURATION"] = NOTE | INVALID_DATA | 0] = "HOLD_HAS_NO_DURATION";
83
85
  ERROR_IDS2[ERROR_IDS2["INVALID_TIME_TUPLE"] = TC | INVALID_DATA | 0] = "INVALID_TIME_TUPLE";
84
86
  ERROR_IDS2[ERROR_IDS2["TIME_MACRO_NOT_FOUND"] = MACRO | INVALID_DATA | 0] = "TIME_MACRO_NOT_FOUND";
85
87
  ERROR_IDS2[ERROR_IDS2["VALUE_MACRO_NOT_FOUND"] = MACRO | INVALID_DATA | 1] = "VALUE_MACRO_NOT_FOUND";
@@ -122,17 +124,28 @@ var ERRORS = {
122
124
  PROTO_PRESENT_IN_NONPARAMETRIC: (macroId) => `'@proto can only be used in parametric Macros. At ${macroId}'`,
123
125
  JAVASCRIPT_SYNTAX_ERROR: (error, macroId) => `JavaScript Syntax Error: ${error.message}. At ${macroId}`,
124
126
  PARAMETRIC_MACRO_REQUIRES_PROTO_KEY: (pos) => `Parametric Macro requires key. At ${pos}`,
125
- MACRO_NOT_PARAMETRIC: (macroId, pos) => `Macro '${macroId}' is not parametric. At ${pos}`
127
+ MACRO_NOT_PARAMETRIC: (macroId, pos) => `Macro '${macroId}' is not parametric. At ${pos}`,
128
+ EVENT_NODE_NOT_DENSE: (pos) => `EventNode is not dense. At ${pos}`,
129
+ HOLD_HAS_NO_DURATION: () => `Hold should have a duration.`
126
130
  };
127
131
 
128
132
  class KPAError extends Error {
129
133
  id;
134
+ fixed = false;
135
+ args;
130
136
  constructor(message, id) {
131
137
  super(message);
132
138
  this.id = id;
133
139
  }
134
- warn() {
140
+ warn(...args) {
135
141
  console.warn(this.stack);
142
+ this.args = args;
143
+ KPAError.buffer.push(this);
144
+ }
145
+ fix(...args) {
146
+ console.warn("[Auto Fixed]" + this.stack);
147
+ this.fixed = true;
148
+ this.args = args;
136
149
  KPAError.buffer.push(this);
137
150
  }
138
151
  static buffer = [];
@@ -1643,7 +1656,7 @@ class EventNodeSequence {
1643
1656
  this.listLength = 1;
1644
1657
  }
1645
1658
  static getDefaultValueFromEventType(type) {
1646
- return type === 4 /* speed */ ? 10 : type === 7 /* scaleX */ || type === 8 /* scaleY */ ? 1 : type === 9 /* text */ ? "" : type === 10 /* color */ ? [0, 0, 0] : 0;
1659
+ return type === 4 /* speed */ ? 10 : type === 7 /* scaleX */ || type === 8 /* scaleY */ ? 1 : type === 9 /* text */ ? "" : type === 10 /* color */ ? [255, 255, 255] : 0;
1647
1660
  }
1648
1661
  static fromRPEJSON(type, data, chart, pos, endValue) {
1649
1662
  const { templateEasingLib: templates } = chart;
@@ -1723,26 +1736,26 @@ class EventNodeSequence {
1723
1736
  err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}] and the previous`).warn();
1724
1737
  }
1725
1738
  if (!TC2.lt(event.startTime, event.endTime)) {
1726
- err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}]`).warn();
1739
+ if (TC2.eq(event.startTime, event.endTime)) {
1740
+ err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}]`).fix();
1741
+ continue;
1742
+ } else {
1743
+ err.EVENT_NODE_TIME_NOT_INCREMENTAL(`${pos}.events[${index}]`).warn();
1744
+ }
1727
1745
  }
1728
- lastEndTime = event.endTime;
1729
1746
  if (lastEnd.type === 0 /* HEAD */) {
1730
1747
  EventNode.connect(lastEnd, start);
1731
- } else if (lastEnd.value === lastEnd.previous.value && lastEnd.previous.evaluator instanceof EasedEvaluator) {
1732
- lastEnd.time = start.time;
1733
- EventNode.connect(lastEnd, start);
1734
- } else if (TC2.eq(lastEnd.time, start.time)) {
1735
- const val = lastEnd.value;
1736
- const midStart = new EventStartNode(lastEnd.time, val);
1737
- const midEnd = new EventEndNode(start.time, val);
1738
- midStart.evaluator = lastEnd.previous.evaluator;
1739
- EventNode.connect(lastEnd, midStart);
1740
- EventNode.connect(midStart, midEnd);
1748
+ } else if (TC2.gt(event.startTime, lastEndTime)) {
1749
+ err.EVENT_NODE_NOT_DENSE(`${pos}.events[${index}]`).warn();
1750
+ const mid = new EventStartNode(lastEndTime, start.value);
1751
+ const midEnd = new EventEndNode(event.startTime, end.value);
1752
+ EventNode.connect(lastEnd, mid);
1753
+ EventNode.connect(mid, midEnd);
1741
1754
  EventNode.connect(midEnd, start);
1742
- listLength++;
1743
1755
  } else {
1744
1756
  EventNode.connect(lastEnd, start);
1745
1757
  }
1758
+ lastEndTime = event.endTime;
1746
1759
  lastEnd = end;
1747
1760
  }
1748
1761
  const last = lastEnd;
@@ -1945,6 +1958,28 @@ class EventNodeSequence {
1945
1958
  dest.initJump();
1946
1959
  return dest;
1947
1960
  }
1961
+ checkErrors() {
1962
+ let currentNode = this.head.next;
1963
+ if (TC2.ne(currentNode.time, [0, 0, 1])) {
1964
+ err.EVENT_NODE_NOT_DENSE(`${this.id}, ${currentNode.time}`).fix();
1965
+ currentNode.time = [0, 0, 1];
1966
+ }
1967
+ const endNode = currentNode.next;
1968
+ if (endNode.type === 1 /* TAIL */) {
1969
+ return;
1970
+ }
1971
+ let lastEnd = endNode;
1972
+ while (true) {
1973
+ const endNode2 = currentNode.next;
1974
+ if (endNode2.type === 1 /* TAIL */) {
1975
+ break;
1976
+ }
1977
+ if (TC2.ne(lastEnd.time, currentNode.time)) {
1978
+ err.EVENT_NODE_NOT_DENSE(`${this.id}, ${currentNode.time}`).warn();
1979
+ }
1980
+ currentNode = currentNode.next.next;
1981
+ }
1982
+ }
1948
1983
  }
1949
1984
  // src/note.ts
1950
1985
  var notePropTypes = {
@@ -3033,6 +3068,15 @@ class BPMEndNode extends EventEndNode {
3033
3068
  spb;
3034
3069
  previous;
3035
3070
  next;
3071
+ get value() {
3072
+ return this.previous.value;
3073
+ }
3074
+ set value(value) {
3075
+ if (!this.previous) {
3076
+ return;
3077
+ }
3078
+ this.previous.value = value;
3079
+ }
3036
3080
  constructor(endTime) {
3037
3081
  super(endTime, null);
3038
3082
  }
@@ -3167,7 +3211,7 @@ class TimeCalculator {
3167
3211
  }
3168
3212
  }
3169
3213
  // src/version.ts
3170
- var VERSION = 211;
3214
+ var VERSION = 212;
3171
3215
  var SCHEMA = "https://cdn.jsdelivr.net/npm/kipphi@2.1.0/chartType2.schema.json";
3172
3216
 
3173
3217
  // src/chart.ts
@@ -3803,9 +3847,20 @@ class OperationList extends EventTarget {
3803
3847
  this.dispatchEvent(new OperationErrorEvent(operation, e));
3804
3848
  return;
3805
3849
  }
3850
+ this.undoneOperations = [];
3851
+ this.operations.push(operation);
3806
3852
  this.dispatchEvent(new OperationEvent("do", operation));
3807
3853
  this.processFlags(operation);
3808
- this.operations.push(operation);
3854
+ }
3855
+ tryDo(returnOperation) {
3856
+ let op;
3857
+ try {
3858
+ op = returnOperation();
3859
+ } catch (e) {
3860
+ this.dispatchEvent(new OperationErrorEvent(op, e));
3861
+ return;
3862
+ }
3863
+ this.do(op);
3809
3864
  }
3810
3865
  processFlags(operation) {
3811
3866
  if (operation.updatesEditor) {
@@ -3970,12 +4025,18 @@ class EventNodePairInsertOperation extends Operation {
3970
4025
  }
3971
4026
  this.updatesFP = updatesFP && targetPrevious.isSpeed();
3972
4027
  }
3973
- do() {
4028
+ do(chart) {
3974
4029
  const [endNode, startNode] = EventNode.insert(this.node, this.tarPrev);
3975
4030
  this.node.parentSeq.updateJump(endNode, startNode);
4031
+ if (this.updatesFP) {
4032
+ this.sequence.updateFloorPositionAfter(this.tarPrev, chart.timeCalculator);
4033
+ }
3976
4034
  }
3977
- undo() {
4035
+ undo(chart) {
3978
4036
  this.sequence.updateJump(...EventNode.removeNodePair(...EventNode.getEndStart(this.node)));
4037
+ if (this.updatesFP) {
4038
+ this.sequence.updateFloorPositionAfter(this.tarPrev, chart.timeCalculator);
4039
+ }
3979
4040
  }
3980
4041
  }
3981
4042
 
@@ -4461,6 +4522,11 @@ class NoteAddOperation extends Operation {
4461
4522
  super();
4462
4523
  this.note = note;
4463
4524
  this.isHold = note.type === 2 /* hold */;
4525
+ if (this.isHold) {
4526
+ if (TC2.lt(note.endTime, note.startTime)) {
4527
+ throw err.HOLD_HAS_NO_DURATION();
4528
+ }
4529
+ }
4464
4530
  this.noteNode = node;
4465
4531
  this.comboDelta = note.isFake ? 0 : 1;
4466
4532
  }
@@ -147,10 +147,21 @@ export class OperationList extends EventTarget {
147
147
  this.dispatchEvent(new OperationErrorEvent(operation, e as Error))
148
148
  return
149
149
  }
150
+ this.undoneOperations = [];
151
+ this.operations.push(operation);
150
152
  this.dispatchEvent(new OperationEvent("do", operation));
151
153
  this.processFlags(operation);
152
- this.operations.push(operation);
153
154
  }
155
+ tryDo(returnOperation: () => Operation) {
156
+ let op: Operation;
157
+ try {
158
+ op = returnOperation();
159
+ } catch (e) {
160
+ this.dispatchEvent(new OperationErrorEvent(op, e as Error))
161
+ return
162
+ }
163
+ this.do(op);
164
+ }
154
165
  processFlags(operation: Operation) {
155
166
 
156
167
  if (operation.updatesEditor) {
@@ -81,12 +81,20 @@ export class EventNodePairInsertOperation <VT extends EventValueESType> extends
81
81
  }
82
82
  this.updatesFP = updatesFP && targetPrevious.isSpeed();
83
83
  }
84
- do() {
84
+ do(chart: Chart) {
85
85
  const [endNode, startNode] = EventNode.insert(this.node, this.tarPrev);
86
86
  this.node.parentSeq.updateJump(endNode, startNode)
87
+ if (this.updatesFP) {
88
+ // updatesFP的校验确保了序列为速度序列
89
+ (this.sequence as SpeedENS).updateFloorPositionAfter(this.tarPrev as EventStartNode, chart.timeCalculator)
90
+ }
87
91
  }
88
- undo() {
92
+ undo(chart: Chart) {
89
93
  this.sequence.updateJump(...EventNode.removeNodePair(...EventNode.getEndStart(this.node)))
94
+ if (this.updatesFP) {
95
+ // updatesFP的校验确保了序列为速度序列
96
+ (this.sequence as SpeedENS).updateFloorPositionAfter(this.tarPrev as EventStartNode, chart.timeCalculator)
97
+ }
90
98
  }
91
99
  }
92
100
 
package/operation/note.ts CHANGED
@@ -140,6 +140,11 @@ export class NoteAddOperation extends Operation {
140
140
  super()
141
141
  this.note = note;
142
142
  this.isHold = note.type === NoteType.hold;
143
+ if (this.isHold) {
144
+ if (TC.lt(note.endTime, note.startTime)) {
145
+ throw err.HOLD_HAS_NO_DURATION();
146
+ }
147
+ }
143
148
  this.noteNode = node;
144
149
  // 一般来说,操作是对于在谱里面的NoteNode,谱外面的不需要操作
145
150
  this.comboDelta = note.isFake ? 0 : +1;
package/package.json CHANGED
@@ -1,26 +1,26 @@
1
1
  {
2
- "name": "kipphi",
3
- "description": "Parse your Phigros Chart(.rpe.json or .kpa.json) into an editor-friendly format.",
4
- "version": "2.1.1",
5
- "author": "Team Zincs (https://github.com/TeamZincs)",
6
- "license": "MIT",
7
- "repository": {
8
- "type": "git",
9
- "url": "https://github.com/TeamZincs/kipphi.git"
10
- },
11
- "module": "index.ts",
12
- "main": "index.ts",
13
- "type": "module",
14
- "devDependencies": {
15
- "@types/bun": "latest"
16
- },
17
- "peerDependencies": {
18
- "typescript": "^5"
19
- },
20
- "exports": {
21
- "./operation": null,
22
- ".": {
23
- "import": "./index.ts"
2
+ "name": "kipphi",
3
+ "description": "Parse your Phigros Chart(.rpe.json or .kpa.json) into an editor-friendly format.",
4
+ "version": "2.1.2",
5
+ "author": "Team Zincs (https://github.com/TeamZincs)",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/TeamZincs/kipphi.git"
10
+ },
11
+ "module": "index.ts",
12
+ "main": "index.ts",
13
+ "type": "module",
14
+ "devDependencies": {
15
+ "@types/bun": "latest"
16
+ },
17
+ "peerDependencies": {
18
+ "typescript": "^5"
19
+ },
20
+ "exports": {
21
+ "./operation": null,
22
+ ".": {
23
+ "import": "./index.ts"
24
+ }
24
25
  }
25
- }
26
26
  }
package/version.ts CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  /// #declaration:global
4
4
 
5
- export const VERSION = 211;
5
+ export const VERSION = 212;
6
6
  export const SCHEMA = "https://cdn.jsdelivr.net/npm/kipphi@2.1.0/chartType2.schema.json"
7
7
 
8
8
 
package/line.ts DELETED
@@ -1,246 +0,0 @@
1
- import { Chart, JudgeLineGroup, BasicEventName, UIName } from "../chart";
2
- import { ExtendedEventTypeName, RGB } from "../chartTypes";
3
- import { EventNodeSequence } from "../event";
4
- import { JudgeLine, ExtendedLayer } from "../judgeline";
5
- import { Operation } from "./basic";
6
-
7
-
8
-
9
- // 有点怪异,感觉破坏了纯净性。不管了(
10
- enum JudgeLinesEditorLayoutType {
11
- ordered = 0b001,
12
- tree = 0b010,
13
- grouped = 0b100
14
- }
15
-
16
-
17
- export class JudgeLineInheritanceChangeOperation extends Operation {
18
- originalValue: JudgeLine | null;
19
- updatesEditor = true;
20
- static REFLOWS = JudgeLinesEditorLayoutType.tree;
21
- reflows = JudgeLineInheritanceChangeOperation.REFLOWS;
22
- constructor(public chart: Chart, public judgeLine: JudgeLine, public value: JudgeLine | null) {
23
- super();
24
- this.originalValue = judgeLine.father;
25
- // 这里只会让它静默失败,外面调用的时候能够在判断一次并抛错误才是最好的
26
- if (JudgeLine.checkinterdependency(judgeLine, value)) {
27
- this.ineffective = true;
28
- }
29
- }
30
- do() {
31
- const line = this.judgeLine;
32
- line.father = this.value;
33
- if (this.originalValue) {
34
- this.originalValue.children.delete(line);
35
- } else {
36
- const index = this.chart.orphanLines.indexOf(line);
37
- if (index >= 0) // Impossible to be false, theoretically
38
- this.chart.orphanLines.splice(index, 1)
39
- }
40
- if (this.value) {
41
- this.value.children.add(line);
42
- } else {
43
- this.chart.orphanLines.push(line);
44
- }
45
- }
46
- undo() {
47
- const line = this.judgeLine;
48
- line.father = this.originalValue;
49
- if (this.originalValue) {
50
- this.originalValue.children.add(line);
51
- } else {
52
- this.chart.orphanLines.push(line);
53
- }
54
- if (this.value) {
55
- this.value.children.delete(line);
56
- } else {
57
- const index = this.chart.orphanLines.indexOf(line);
58
- if (index >= 0) // Impossible to be false, theoretically
59
- this.chart.orphanLines.splice(index, 1)
60
- }
61
- }
62
- }
63
-
64
- export class JudgeLineRenameOperation extends Operation {
65
- updatesEditor = true;
66
- originalValue: string;
67
- constructor(public judgeLine: JudgeLine, public value: string) {
68
- super();
69
- this.originalValue = judgeLine.name;
70
- }
71
- do() {
72
- this.judgeLine.name = this.value;
73
- }
74
- undo() {
75
- this.judgeLine.name = this.originalValue;
76
- }
77
- }
78
-
79
- type JudgeLinePropName = "name" | "rotatesWithFather" | "anchor" | "texture" | "cover" | "zOrder";
80
-
81
- export class JudgeLinePropChangeOperation<T extends JudgeLinePropName> extends Operation {
82
- updatesEditor = true;
83
- originalValue: JudgeLine[T];
84
- constructor(public judgeLine: JudgeLine, public field: T, public value: JudgeLine[T]) {
85
- super();
86
- this.originalValue = judgeLine[field];
87
- }
88
- do() {
89
- this.judgeLine[this.field] = this.value;
90
- }
91
- undo() {
92
- this.judgeLine[this.field] = this.originalValue;
93
- }
94
- }
95
-
96
- export class JudgeLineRegroupOperation extends Operation {
97
- updatesEditor = true;
98
- reflows = JudgeLinesEditorLayoutType.grouped;
99
- originalValue: JudgeLineGroup;
100
- constructor(public judgeLine: JudgeLine, public value: JudgeLineGroup) {
101
- super();
102
- this.originalValue = judgeLine.group;
103
- }
104
- do() {
105
- this.judgeLine.group = this.value;
106
- this.value.add(this.judgeLine);
107
- this.originalValue.remove(this.judgeLine);
108
- }
109
- undo() {
110
- this.judgeLine.group = this.originalValue;
111
- this.originalValue.add(this.judgeLine);
112
- this.value.remove(this.judgeLine);
113
- }
114
- }
115
-
116
- export class JudgeLineCreateOperation extends Operation {
117
- reflows = JudgeLinesEditorLayoutType.grouped | JudgeLinesEditorLayoutType.tree | JudgeLinesEditorLayoutType.ordered;
118
- // 之前把=写成了:半天不知道咋错了
119
- constructor(public chart: Chart, public judgeLine: JudgeLine) {
120
- super();
121
- }
122
- do() {
123
- const id = this.chart.judgeLines.length;
124
- this.judgeLine.id = id;
125
- this.chart.judgeLines.push(this.judgeLine);
126
- this.chart.orphanLines.push(this.judgeLine);
127
- this.chart.judgeLineGroups[0].add(this.judgeLine);
128
- }
129
- undo() {
130
- this.chart.judgeLineGroups[0].remove(this.judgeLine);
131
- this.chart.judgeLines.splice(this.chart.judgeLines.indexOf(this.judgeLine), 1);
132
- this.chart.orphanLines.splice(this.chart.orphanLines.indexOf(this.judgeLine), 1);
133
- }
134
- }
135
-
136
- export class JudgeLineDeleteOperation extends Operation {
137
- readonly originalGroup: JudgeLineGroup;
138
- constructor(public chart: Chart, public judgeLine: JudgeLine) {
139
- super();
140
- if (!this.chart.judgeLines.includes(this.judgeLine)) {
141
- this.ineffective = true;
142
- }
143
- this.originalGroup = judgeLine.group;
144
- }
145
- do() {
146
- this.chart.judgeLines.splice(this.chart.judgeLines.indexOf(this.judgeLine), 1);
147
- if (this.chart.orphanLines.includes(this.judgeLine)) {
148
- this.chart.orphanLines.splice(this.chart.orphanLines.indexOf(this.judgeLine), 1);
149
- }
150
- this.originalGroup.remove(this.judgeLine);
151
- }
152
- undo() {
153
- this.chart.judgeLines.push(this.judgeLine);
154
- this.chart.orphanLines.push(this.judgeLine);
155
- this.originalGroup.add(this.judgeLine);
156
- }
157
- }
158
-
159
-
160
-
161
- export class JudgeLineENSChangeOperation extends Operation {
162
- originalValue: EventNodeSequence;
163
- constructor(public judgeLine: JudgeLine, public layerId: number, public typeStr: BasicEventName, public value: EventNodeSequence) {
164
- super();
165
- this.originalValue = judgeLine.eventLayers[layerId][typeStr];
166
- }
167
- do() {
168
- this.judgeLine.eventLayers[this.layerId][this.typeStr] = this.value;
169
- }
170
- undo() {
171
- this.judgeLine.eventLayers[this.layerId][this.typeStr] = this.originalValue;
172
- }
173
- }
174
-
175
-
176
- export type ENSOfTypeName<T extends ExtendedEventTypeName> = {
177
- "scaleX": EventNodeSequence<number>,
178
- "scaleY": EventNodeSequence<number>
179
- "text": EventNodeSequence<string>,
180
- "color": EventNodeSequence<RGB>
181
- }[T]
182
- export class JudgeLineExtendENSChangeOperation<T extends ExtendedEventTypeName> extends Operation {
183
- originalValue: ENSOfTypeName<T>;
184
- constructor(public judgeLine: JudgeLine, public typeStr: T, public value: ENSOfTypeName<T> | null) {
185
- super();
186
- this.originalValue = judgeLine.extendedLayer[typeStr satisfies keyof ExtendedLayer] as ENSOfTypeName<T>;
187
- }
188
- do() {
189
- this.judgeLine.extendedLayer[this.typeStr] = this.value
190
- }
191
- undo() {
192
- this.judgeLine.extendedLayer[this.typeStr] = this.originalValue
193
- }
194
-
195
- }
196
-
197
- export class UIAttachOperation extends Operation {
198
- updatesEditor = true;
199
- constructor(public chart: Chart, public judgeLine: JudgeLine, public ui: UIName) {
200
- super();
201
- }
202
- do() {
203
- this.chart.attachUIToLine(this.ui, this.judgeLine);
204
- }
205
- undo() {
206
- this.chart.detachUI(this.ui);
207
- }
208
- }
209
-
210
- export class UIDetachOperation extends Operation {
211
- updatesEditor = true;
212
- judgeLine: JudgeLine;
213
- constructor(public chart: Chart, public ui: UIName) {
214
- super();
215
- if (chart[`${ui}Attach` satisfies keyof Chart]) {
216
- this.judgeLine = chart[`${ui}Attach` satisfies keyof Chart];
217
- } else {
218
- this.ineffective = true;
219
- }
220
- }
221
- do() {
222
- this.chart.detachUI(this.ui);
223
- }
224
- undo() {
225
- this.chart.attachUIToLine(this.ui, this.judgeLine);
226
- }
227
- }
228
-
229
- export class JudgeLineDetachAllUIOperation extends Operation {
230
- updatesEditor = true;
231
- uinames: UIName[];
232
- constructor(public chart: Chart, public judgeLine: JudgeLine) {
233
- super();
234
- this.uinames = chart.queryJudgeLineUI(this.judgeLine);
235
- }
236
- do() {
237
- for (const ui of this.uinames) {
238
- this.chart.detachUI(ui);
239
- }
240
- }
241
- undo() {
242
- for (const ui of this.uinames) {
243
- this.chart.attachUIToLine(ui, this.judgeLine);
244
- }
245
- }
246
- }