sonolus-next-rush-engine 1.0.12 → 1.0.14

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.
@@ -348,6 +348,8 @@ export const extendedToLevelData = (data, offset = 0) => {
348
348
  note.set('size', getNum(e, 'size', 0.0));
349
349
  note.set('direction', flickDirectionMapping[getNum(e, 'direction', 0)] ?? FlickDirection.UP_OMNI);
350
350
  note.set('segmentKind', ConnectorKind.ACTIVE_NORMAL);
351
+ note.set('segmentAlpha', 1);
352
+ note.set('segmentLayer', 0);
351
353
  note.set('isAttached', 0);
352
354
  note.set('connectorEase', 0);
353
355
  note.set('isSeparator', 0);
@@ -363,6 +365,10 @@ export const extendedToLevelData = (data, offset = 0) => {
363
365
  return notesByName.get(ref);
364
366
  return undefined;
365
367
  }
368
+ function getTimeScaleAt(changes, beat) {
369
+ const change = [...changes].reverse().find((change) => change.beat < beat - 1e-6);
370
+ return change?.timeScale ?? 1;
371
+ }
366
372
  function shouldUseStartAsHead(startRef, headRef) {
367
373
  if (startRef === headRef)
368
374
  return false;
@@ -404,8 +410,12 @@ export const extendedToLevelData = (data, offset = 0) => {
404
410
  const headChanges = getTSGChanges(headTsgRef);
405
411
  const tailChanges = getTSGChanges(tailTsgRef);
406
412
  const splitBeats = headChanges
407
- .map(({ beat }) => beat)
408
- .filter((beat) => headBeat + 1e-6 < beat && beat < tailBeat - 1e-6);
413
+ .filter((change) => {
414
+ if (!(headBeat + 1e-6 < change.beat && change.beat < tailBeat - 1e-6))
415
+ return false;
416
+ return !nearlyEqual(change.timeScale, getTimeScaleAt(headChanges, change.beat));
417
+ })
418
+ .map(({ beat }) => beat);
409
419
  if (splitBeats.length === 0)
410
420
  return [];
411
421
  const headScaledTime = timeToScaledTime(beatToTime(headBeat), headChanges);
@@ -433,7 +443,9 @@ export const extendedToLevelData = (data, offset = 0) => {
433
443
  const scaledTime = timeToScaledTime(beatToTime(beat), headChanges);
434
444
  const frac = unlerp(headScaledTime, tailScaledTime, scaledTime);
435
445
  const easedFrac = applyEase(ease, frac);
436
- return createConnectorAnchor(beat, lerp(headLane, tailLane, easedFrac), lerp(headSize, tailSize, easedFrac), tsg, kind);
446
+ const lane = lerp(headLane, tailLane, easedFrac);
447
+ const size = lerp(headSize, tailSize, easedFrac);
448
+ return createConnectorAnchor(beat, lane, size, tsg, kind);
437
449
  });
438
450
  }
439
451
  function isReverseHiddenPopConnector(headOriginal, tailOriginal) {
@@ -445,14 +457,39 @@ export const extendedToLevelData = (data, offset = 0) => {
445
457
  return false;
446
458
  return getNum(tailOriginal, '#BEAT') < getNum(headOriginal, '#BEAT') - 1e-6;
447
459
  }
460
+ function getUltimateTailRef(startRef, tailRef) {
461
+ let ultimateTailRef = tailRef;
462
+ let ultimateTailBeat = getNum(resolveOriginal(ext, tailRef) ?? { archetype: '', data: [] }, '#BEAT');
463
+ const visited = new Set();
464
+ function visit(headRef) {
465
+ const key = `${String(startRef)}|${String(headRef)}`;
466
+ if (headRef === undefined || visited.has(key))
467
+ return;
468
+ visited.add(key);
469
+ const nextConnectors = ext.connectors.filter((c) => getField(c.e, 'head') === headRef && getField(c.e, 'start') === startRef);
470
+ if (nextConnectors.length === 0) {
471
+ const beat = getNum(resolveOriginal(ext, headRef) ?? { archetype: '', data: [] }, '#BEAT');
472
+ if (beat >= ultimateTailBeat) {
473
+ ultimateTailBeat = beat;
474
+ ultimateTailRef = headRef;
475
+ }
476
+ return;
477
+ }
478
+ for (const nextConnector of nextConnectors) {
479
+ visit(getField(nextConnector.e, 'tail'));
480
+ }
481
+ }
482
+ visit(tailRef);
483
+ return ultimateTailRef;
484
+ }
448
485
  for (const { idx, e } of ext.connectors) {
449
486
  const startRef = getField(e, 'start');
450
487
  const headRef = getField(e, 'head');
451
488
  const tailRef = getField(e, 'tail');
452
- const rawHead = getNote(headRef);
453
489
  const tail = getNote(tailRef);
454
490
  const rawHeadOriginal = resolveOriginal(ext, headRef);
455
491
  const tailOriginal = resolveOriginal(ext, tailRef);
492
+ const rawHead = getNote(headRef);
456
493
  const activeHead = getNote(startRef);
457
494
  const usesStartAsHead = shouldUseStartAsHead(startRef, headRef);
458
495
  const head = usesStartAsHead ? activeHead : rawHead;
@@ -460,21 +497,7 @@ export const extendedToLevelData = (data, offset = 0) => {
460
497
  const endRef = getField(e, 'end');
461
498
  let activeTail = getNote(endRef);
462
499
  if (!activeTail) {
463
- const currentTailRef = getField(e, 'tail');
464
- let ultimateTailRef = currentTailRef;
465
- const visited = new Set();
466
- while (ultimateTailRef !== undefined && !visited.has(ultimateTailRef)) {
467
- visited.add(ultimateTailRef);
468
- const nextConn = ext.connectors.find((c) => getField(c.e, 'head') === ultimateTailRef &&
469
- getField(c.e, 'start') === startRef);
470
- if (nextConn) {
471
- ultimateTailRef = getField(nextConn.e, 'tail');
472
- }
473
- else {
474
- break;
475
- }
476
- }
477
- activeTail = getNote(ultimateTailRef);
500
+ activeTail = getNote(getUltimateTailRef(startRef, getField(e, 'tail')));
478
501
  }
479
502
  if (!activeTail) {
480
503
  activeTail = tail;
@@ -490,6 +513,7 @@ export const extendedToLevelData = (data, offset = 0) => {
490
513
  : [];
491
514
  const segmentEase = splitAnchors.length > 0 ? EaseType.LINEAR : ease;
492
515
  const segmentNotes = [head, ...splitAnchors, tail];
516
+ const segments = [];
493
517
  if (reverseHiddenPopConnector && rawHeadOriginal && tailOriginal) {
494
518
  const segmentHead = createConnectorAnchor(getNum(rawHeadOriginal, '#BEAT'), getNum(rawHeadOriginal, 'lane'), getNum(rawHeadOriginal, 'size'), getTSG(getField(rawHeadOriginal, 'timeScaleGroup')) ?? tsg, kind);
495
519
  const connector = new EntityBuilder('Connector');
@@ -501,6 +525,7 @@ export const extendedToLevelData = (data, offset = 0) => {
501
525
  connector.set('activeHead', activeHead);
502
526
  connector.set('activeTail', activeTail);
503
527
  finalEntities.push(connector);
528
+ segments.push({ head: segmentHead, tail });
504
529
  }
505
530
  else {
506
531
  for (let i = 0; i < segmentNotes.length - 1; i++) {
@@ -514,13 +539,16 @@ export const extendedToLevelData = (data, offset = 0) => {
514
539
  connector.set('activeHead', activeHead);
515
540
  connector.set('activeTail', activeTail);
516
541
  finalEntities.push(connector);
542
+ segments.push({ head: segmentHead, tail: segmentTail });
517
543
  }
518
544
  }
519
- const connectorLink = new EntityBuilder('Connector');
520
- connectorLink.set('head', head);
521
- connectorLink.set('tail', tail);
522
- connectorLink.set('activeHead', activeHead);
523
- connectorLink.set('activeTail', activeTail);
545
+ const connectorLink = {
546
+ head,
547
+ tail,
548
+ activeHead,
549
+ activeTail,
550
+ segments,
551
+ };
524
552
  for (const segmentHead of segmentNotes.slice(0, -1)) {
525
553
  segmentHead.set('connectorEase', segmentEase);
526
554
  segmentHead.set('segmentKind', kind);
@@ -540,6 +568,18 @@ export const extendedToLevelData = (data, offset = 0) => {
540
568
  return connectorsByName.get(ref);
541
569
  return undefined;
542
570
  }
571
+ function getAttachSegment(conn, beat) {
572
+ for (const segment of conn.segments) {
573
+ const headBeat = segment.head.getBeat();
574
+ const tailBeat = segment.tail.getBeat();
575
+ const minBeat = Math.min(headBeat, tailBeat);
576
+ const maxBeat = Math.max(headBeat, tailBeat);
577
+ if (minBeat - 1e-6 <= beat && beat <= maxBeat + 1e-6) {
578
+ return segment;
579
+ }
580
+ }
581
+ return { head: conn.head, tail: conn.tail };
582
+ }
543
583
  for (const [idx, note] of notesByIndex.entries()) {
544
584
  const e = ext.get(idx);
545
585
  const tsgRef = getField(e, 'timeScaleGroup');
@@ -548,17 +588,18 @@ export const extendedToLevelData = (data, offset = 0) => {
548
588
  const attachRef = getField(e, 'attach');
549
589
  if (attachRef !== undefined) {
550
590
  const attachConn = getConn(attachRef);
551
- if (attachConn?.refs.head && attachConn.refs.tail) {
552
- note.set('attachHead', attachConn.refs.head);
553
- note.set('attachTail', attachConn.refs.tail);
591
+ if (attachConn) {
592
+ const attachSegment = getAttachSegment(attachConn, getNum(e, '#BEAT'));
593
+ note.set('attachHead', attachSegment.head);
594
+ note.set('attachTail', attachSegment.tail);
554
595
  note.set('isAttached', 1);
555
596
  }
556
597
  }
557
598
  const slideRef = getField(e, 'slide');
558
599
  if (slideRef !== undefined) {
559
600
  const slideConn = getConn(slideRef);
560
- if (slideConn?.refs.activeHead) {
561
- note.set('activeHead', slideConn.refs.activeHead);
601
+ if (slideConn) {
602
+ note.set('activeHead', slideConn.activeHead);
562
603
  }
563
604
  }
564
605
  }
@@ -575,6 +616,7 @@ export const extendedToLevelData = (data, offset = 0) => {
575
616
  const anchorsByBeat = new Map();
576
617
  const anchorPositions = new Map();
577
618
  function getAnchor(beat, lane, size, tsg, pos, segmentKind = -1, segmentAlpha = -1, connectorEase = -1) {
619
+ const anchorTsg = tsg ?? defaultTsg;
578
620
  const anchors = anchorsByBeat.get(beat) || [];
579
621
  for (const anchor of anchors) {
580
622
  const positions = anchorPositions.get(anchor);
@@ -582,7 +624,7 @@ export const extendedToLevelData = (data, offset = 0) => {
582
624
  continue;
583
625
  if (anchor.values.lane === lane &&
584
626
  anchor.values.size === size &&
585
- anchor.refs['#TIMESCALE_GROUP'] === tsg &&
627
+ anchor.refs['#TIMESCALE_GROUP'] === anchorTsg &&
586
628
  (segmentKind === -1 ||
587
629
  anchor.values.segmentKind === segmentKind ||
588
630
  anchor.values.segmentKind === -1) &&
@@ -606,10 +648,11 @@ export const extendedToLevelData = (data, offset = 0) => {
606
648
  newAnchor.set('#BEAT', beat);
607
649
  newAnchor.set('lane', lane);
608
650
  newAnchor.set('size', size);
609
- if (tsg)
610
- newAnchor.set('#TIMESCALE_GROUP', tsg);
651
+ newAnchor.set('direction', FlickDirection.UP_OMNI);
652
+ newAnchor.set('#TIMESCALE_GROUP', anchorTsg);
611
653
  newAnchor.set('segmentKind', segmentKind);
612
654
  newAnchor.set('segmentAlpha', segmentAlpha);
655
+ newAnchor.set('segmentLayer', 0);
613
656
  newAnchor.set('connectorEase', connectorEase);
614
657
  newAnchor.set('isAttached', 0);
615
658
  newAnchor.set('isSeparator', 0);
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ import { USC } from './usc/index.js';
7
7
  export * from './usc/index.js';
8
8
  export { type ExtendedEntityData, type ExtendedEntityDataField, extendedToLevelData, mmwsToUSC, susToUSC, ucmmwsToLevelData, uscToLevelData, };
9
9
  export declare const convertToLevelData: (input: string | Uint8Array | USC | LevelData, offset?: number) => LevelData;
10
- export declare const version = "1.0.12";
10
+ export declare const version = "1.0.14";
11
11
  export declare const databaseEngineItem: {
12
12
  readonly name: "next-rush";
13
13
  readonly version: 13;
package/dist/index.js CHANGED
@@ -65,7 +65,7 @@ export const convertToLevelData = (input, offset = 0) => {
65
65
  }
66
66
  return uscToLevelData(usc, offset, true, true);
67
67
  };
68
- export const version = '1.0.12';
68
+ export const version = '1.0.14';
69
69
  export const databaseEngineItem = {
70
70
  name: 'next-rush',
71
71
  version: 13,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sonolus-next-rush-engine",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Perspective-lane rhythm game for Sonolus",
5
5
  "author": "Hyeon2",
6
6
  "repository": {