danoniplus 32.6.1 → 33.0.0

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/js/danoni_main.js CHANGED
@@ -4,12 +4,12 @@
4
4
  *
5
5
  * Source by tickle
6
6
  * Created : 2018/10/08
7
- * Revised : 2023/07/08
7
+ * Revised : 2023/07/29
8
8
  *
9
9
  * https://github.com/cwtickle/danoniplus
10
10
  */
11
- const g_version = `Ver 32.6.1`;
12
- const g_revisedDate = `2023/07/08`;
11
+ const g_version = `Ver 33.0.0`;
12
+ const g_revisedDate = `2023/07/29`;
13
13
 
14
14
  // カスタム用バージョン (danoni_custom.js 等で指定可)
15
15
  let g_localVersion = ``;
@@ -47,6 +47,7 @@ const current = _ => {
47
47
  return targetScript.src;
48
48
  };
49
49
  const g_rootPath = current().match(/(^.*\/)/)[0];
50
+ const g_workPath = new URL(location.href).href.match(/(^.*\/)/)[0];
50
51
  const g_remoteFlg = g_rootPath.match(`^https://cwtickle.github.io/danoniplus/`) !== null;
51
52
  const g_randTime = Date.now();
52
53
 
@@ -348,7 +349,7 @@ const hasArrayList = (_data, _length = 1) => _data !== undefined && _data.length
348
349
  * 改行コード区切りの配列展開
349
350
  * @param {string} _str
350
351
  */
351
- const splitLF = _str => _str.split(`\r`).join(`\n`).split(`\n`);
352
+ const splitLF = _str => _str?.split(`\r`).join(`\n`).split(`\n`);
352
353
 
353
354
  /**
354
355
  * 改行コード区切りを本来の区切り文字に変換して配列展開
@@ -356,7 +357,7 @@ const splitLF = _str => _str.split(`\r`).join(`\n`).split(`\n`);
356
357
  * @param {string} _str
357
358
  * @param {string} _delim
358
359
  */
359
- const splitLF2 = (_str, _delim = `$`) => splitLF(_str).filter(val => val !== ``).join(_delim).split(_delim);
360
+ const splitLF2 = (_str, _delim = `$`) => splitLF(_str)?.filter(val => val !== ``).join(_delim).split(_delim);
360
361
 
361
362
  /**
362
363
  * 重複を排除した配列の生成
@@ -767,32 +768,66 @@ const loadMultipleFiles2 = async (_fileData, _loadType) => {
767
768
  }));
768
769
  };
769
770
 
770
- /**
771
- * 入力されたパスを、ディレクトリとそれ以外に分割
772
- * 返却値:[ファイルキーワード, ルートディレクトリ]
773
- * @param {string} _path
774
- * @param {number} _pos
775
- * @param {string} _directory
776
- */
777
- const getFolderAndType = (_path, _pos, _directory = ``) => {
778
- const rootPath = (_directory === `` ? `` : g_rootPath);
779
- return (_pos > 0 ? [_path.substring(_pos + 1), `${rootPath}${_path.substring(0, _pos)}/`] : [_path, `${rootPath}${_directory}`]);
780
- };
781
-
782
771
  /**
783
772
  * 与えられたパスより、キーワードとディレクトリに分割
784
- * カレントディレクトリ指定がある場合を考慮して、処理を分けている
785
773
  * 返却値:[ファイルキーワード, ルートディレクトリ]
786
774
  * @param {string} _fileName
787
- * @param {string} _directory
775
+ * @param {string} _directory
788
776
  */
789
777
  const getFilePath = (_fileName, _directory = ``) => {
790
- if (_fileName.indexOf(C_MRK_CURRENT_DIRECTORY) !== -1) {
791
- const tmpType = _fileName.split(C_MRK_CURRENT_DIRECTORY)[1];
792
- return getFolderAndType(tmpType, tmpType.indexOf(`/`));
778
+ let fullPath;
779
+ if (_fileName.startsWith(C_MRK_CURRENT_DIRECTORY)) {
780
+ fullPath = `${g_workPath}${_fileName.slice(C_MRK_CURRENT_DIRECTORY.length)}`;
793
781
  } else {
794
- return getFolderAndType(_fileName, _fileName.lastIndexOf(`/`), _directory);
782
+ fullPath = `${g_rootPath}${_directory}${_fileName}`;
795
783
  }
784
+ const dirPos = fullPath.lastIndexOf(`/`);
785
+ return [fullPath.slice(dirPos + 1), fullPath.slice(0, dirPos + 1)];
786
+ }
787
+
788
+ /**
789
+ * 画像ファイルの存在チェック後、プリロードする処理
790
+ * @param {string} _imgPath
791
+ * @param {string} directory
792
+ * @param {boolean} syncBackPath
793
+ * @returns
794
+ */
795
+ const preloadImgFile = (_imgPath, { directory = ``, syncBackPath = true } = {}) => {
796
+
797
+ let imgPath = _imgPath;
798
+ if (g_headerObj.autoPreload) {
799
+ if (checkImage(_imgPath)) {
800
+ if (syncBackPath) {
801
+ const [file, dir] = getFilePath(_imgPath, directory);
802
+ imgPath = `${dir}${file}`;
803
+ }
804
+ preloadFile(`image`, imgPath);
805
+ }
806
+ }
807
+ return imgPath;
808
+ };
809
+
810
+ /**
811
+ * 画像パス部分の取得
812
+ * @param {string} _str
813
+ * @returns
814
+ */
815
+ const getImageUrlPath = _str => {
816
+ const matches = _str?.match(/url\("([^"]*)"\)/);
817
+ return matches && matches.length >= 2 ? matches[1] : ``;
818
+ };
819
+
820
+ /**
821
+ * カレントディレクトリを含む文字列を置換し、変更後の文字列を作成
822
+ * @param {string} _str
823
+ */
824
+ const reviseCssText = _str => {
825
+ if (getImageUrlPath(_str) !== ``) {
826
+ const imgOriginal = getImageUrlPath(_str);
827
+ const imgPath = preloadImgFile(imgOriginal);
828
+ return replaceStr(_str, [[imgOriginal, imgPath]]);
829
+ }
830
+ return _str;
796
831
  };
797
832
 
798
833
  /*-----------------------------------------------------------*/
@@ -890,7 +925,17 @@ const makeColorGradation = (_colorStr, { _defaultColorgrd = g_headerObj.defaultC
890
925
  const alphaVal = (_shadowFlg && _objType !== `frz`) ? `80` : (_objType === `titleArrow` ? `40` : ``);
891
926
 
892
927
  let convertColorStr = ``;
893
- const tmpColorStr = _colorStr.split(`@`);
928
+ const tmpBackgroundStr = _colorStr.split(`;`);
929
+
930
+ // 色情報以外の部分を退避
931
+ const addData = tmpBackgroundStr[1] !== undefined ? tmpBackgroundStr.slice(1).join(` `) : ``;
932
+ if ([``, `-`, `none`].includes(tmpBackgroundStr[0]) ||
933
+ tmpBackgroundStr[0].startsWith(`url(`) || tmpBackgroundStr[0].startsWith(`var(`)) {
934
+ return addData;
935
+ }
936
+
937
+ // 色情報からグラデーションを作成
938
+ const tmpColorStr = tmpBackgroundStr[0].split(`@`);
894
939
  const colorArray = tmpColorStr[0].split(`:`);
895
940
  for (let j = 0; j < colorArray.length; j++) {
896
941
  colorArray[j] = colorCdPadding(_colorCdPaddingUse, colorToHex(colorArray[j].replaceAll(`0x`, `#`)));
@@ -917,7 +962,7 @@ const makeColorGradation = (_colorStr, { _defaultColorgrd = g_headerObj.defaultC
917
962
  convertColorStr += `${colorArray.join(', ')}`;
918
963
  }
919
964
 
920
- return `${gradationType}(${convertColorStr})`;
965
+ return `${hasVal(addData) ? `${addData} ` : ''}${gradationType}(${convertColorStr})`;
921
966
  };
922
967
 
923
968
  /*-----------------------------------------------------------*/
@@ -1406,8 +1451,60 @@ const drawDefaultBackImage = _key => {
1406
1451
  } else {
1407
1452
  createEmptySprite(divRoot, `divBack`);
1408
1453
  }
1454
+
1455
+ // CSSスタイルの初期化
1456
+ Object.keys(g_cssBkProperties).forEach(prop =>
1457
+ document.documentElement.style.setProperty(prop, g_cssBkProperties[prop]));
1458
+
1459
+ Object.keys(g_headerObj).filter(val => val.startsWith(`--`) && hasVal(g_headerObj[val])).forEach(prop =>
1460
+ document.documentElement.style.setProperty(prop, getCssCustomProperty(prop, g_headerObj[prop])));
1461
+
1409
1462
  };
1410
1463
 
1464
+ /**
1465
+ * CSSカスタムプロパティの値を作成
1466
+ * @param {string} _prop
1467
+ * @param {string} _propData
1468
+ */
1469
+ const getCssCustomProperty = (_prop, _propData) =>
1470
+ document.documentElement.style.getPropertyValue(_propData) !== `` ?
1471
+ document.documentElement.style.getPropertyValue(_propData) :
1472
+ g_cssBkProperties[_propData] !== undefined ?
1473
+ g_cssBkProperties[_propData] :
1474
+ _prop.endsWith(`-x`) ? _propData : reviseCssText(makeColorGradation(_propData, { _defaultColorgrd: false }));
1475
+
1476
+ /**
1477
+ * CSSカスタムプロパティの値をオブジェクトへ退避
1478
+ */
1479
+ const getCssCustomProperties = _ => {
1480
+ try {
1481
+ const htmlStyle = document.documentElement.computedStyleMap();
1482
+ for (const [propertyName, value] of htmlStyle.entries()) {
1483
+ if (/^--/.test(propertyName)) {
1484
+ g_cssBkProperties[propertyName] = value.toString();
1485
+ }
1486
+ }
1487
+ } catch (error) {
1488
+ // FirefoxではcomputedStyleMapが使えないため、
1489
+ // CSSの全スタイルシート定義から :root がセレクタのルールを抽出し、カスタムプロパティを抽出
1490
+ const sheets = document.styleSheets;
1491
+ for (const sheet of sheets) {
1492
+ if (!g_isFile && sheet.cssRules) {
1493
+ for (const rule of sheet.cssRules) {
1494
+ if (rule.selectorText === ':root') {
1495
+ for (let i = 0; i < rule.style.length; i++) {
1496
+ const propertyName = rule.style.item(i);
1497
+ if (/^--/.test(propertyName)) {
1498
+ g_cssBkProperties[propertyName] = rule.style.getPropertyValue(propertyName);
1499
+ }
1500
+ }
1501
+ }
1502
+ }
1503
+ }
1504
+ }
1505
+ }
1506
+ }
1507
+
1411
1508
  /**
1412
1509
  * 背景・マスク用画像の描画
1413
1510
  * @param {object} _obj
@@ -1450,7 +1547,7 @@ const makeSpriteText = _obj => {
1450
1547
  * @param {object, array} _obj
1451
1548
  */
1452
1549
  const checkDuplicatedObjects = _obj => {
1453
- let addFrame = 0;
1550
+ let dataCnts = 0;
1454
1551
  if (_obj === undefined) {
1455
1552
  _obj = [];
1456
1553
  _obj[0] = [];
@@ -1458,17 +1555,17 @@ const checkDuplicatedObjects = _obj => {
1458
1555
  for (let m = 1; ; m++) {
1459
1556
  if (_obj[m] === undefined) {
1460
1557
  _obj[m] = [];
1461
- addFrame = m;
1558
+ dataCnts = m;
1462
1559
  break;
1463
1560
  }
1464
1561
  }
1465
1562
  }
1466
- return [_obj, addFrame];
1563
+ return [_obj, dataCnts];
1467
1564
  };
1468
1565
 
1469
1566
  /**
1470
1567
  * 多層スプライトデータの作成処理
1471
- * @param {array} _data
1568
+ * @param {string} _data
1472
1569
  * @param {function} _calcFrame
1473
1570
  */
1474
1571
  const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
@@ -1485,12 +1582,8 @@ const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
1485
1582
  }
1486
1583
 
1487
1584
  // 値チェックとエスケープ処理
1488
- let tmpFrame;
1489
- if (setIntVal(tmpSpriteData[0], -1) === 0) {
1490
- tmpFrame = 0;
1491
- } else {
1492
- tmpFrame = roundZero(_calcFrame(setVal(tmpSpriteData[0], 200, C_TYP_CALC)));
1493
- }
1585
+ const tmpFrame = setIntVal(tmpSpriteData[0], -1) === 0 ? 0 :
1586
+ roundZero(_calcFrame(setVal(tmpSpriteData[0], 200, C_TYP_CALC)));
1494
1587
  const tmpDepth = (tmpSpriteData[1] === C_FLG_ALL ? C_FLG_ALL : setVal(tmpSpriteData[1], 0, C_TYP_CALC));
1495
1588
  if (tmpDepth !== C_FLG_ALL && tmpDepth > maxDepth) {
1496
1589
  maxDepth = tmpDepth;
@@ -1512,23 +1605,15 @@ const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
1512
1605
  if (setVal(tmpSpriteData[11], g_presetObj.animationFillMode) !== undefined) {
1513
1606
  tmpObj.animationFillMode = setVal(tmpSpriteData[11], g_presetObj.animationFillMode);
1514
1607
  }
1515
- if (g_headerObj.autoPreload) {
1516
- if (checkImage(tmpObj.path)) {
1517
- if (g_headerObj.syncBackPath) {
1518
- const [file, dir] = getFilePath(tmpObj.path, `./`);
1519
- tmpObj.path = `${dir}${file}`;
1520
- }
1521
- preloadFile(`image`, tmpObj.path);
1522
- }
1523
- }
1608
+ tmpObj.path = preloadImgFile(tmpObj.path, { syncBackPath: g_headerObj.syncBackPath });
1524
1609
 
1525
- let addFrame = 0;
1526
- [spriteData[tmpFrame], addFrame] =
1610
+ let dataCnts = 0;
1611
+ [spriteData[tmpFrame], dataCnts] =
1527
1612
  checkDuplicatedObjects(spriteData[tmpFrame]);
1528
1613
 
1529
1614
  const emptyPatterns = [``, `[loop]`, `[jump]`];
1530
1615
  const colorObjFlg = tmpSpriteData[2]?.startsWith(`[c]`) || false;
1531
- const spriteFrameData = spriteData[tmpFrame][addFrame] = {
1616
+ const spriteFrameData = spriteData[tmpFrame][dataCnts] = {
1532
1617
  depth: tmpDepth,
1533
1618
  };
1534
1619
 
@@ -1542,7 +1627,7 @@ const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
1542
1627
  animationName: tmpObj.animationName,
1543
1628
  animationDuration: `${tmpObj.animationDuration}s`,
1544
1629
  };
1545
- spriteFrameData.colorObjId = `${tmpFrame}_${addFrame}`;
1630
+ spriteFrameData.colorObjId = `${tmpFrame}_${dataCnts}`;
1546
1631
  spriteFrameData.colorObjClass = setVal(tmpObj.class, undefined);
1547
1632
  if (tmpObj.animationFillMode !== undefined) {
1548
1633
  spriteFrameData.colorObjInfo.animationFillMode = tmpObj.animationFillMode;
@@ -1564,6 +1649,34 @@ const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
1564
1649
  return [spriteData, maxDepth];
1565
1650
  };
1566
1651
 
1652
+ /**
1653
+ * スタイル変更データの作成処理
1654
+ * @param {string} _data
1655
+ * @param {function} _calcFrame
1656
+ * @returns
1657
+ */
1658
+ const makeStyleData = (_data, _calcFrame = _frame => _frame) => {
1659
+ const spriteData = [];
1660
+ splitLF(_data).filter(data => hasVal(data)).forEach(tmpData => {
1661
+ const tmpSpriteData = tmpData.split(`,`);
1662
+
1663
+ // カスタムプロパティの名称(--始まり)で無い場合はコメントと見做してスキップ
1664
+ if (tmpSpriteData.length <= 1 || !tmpSpriteData[1].startsWith(`--`)) {
1665
+ return;
1666
+ }
1667
+ const tmpFrame = setIntVal(tmpSpriteData[0], -1) === 0 ? 0 :
1668
+ roundZero(_calcFrame(setVal(tmpSpriteData[0], 200, C_TYP_CALC)));
1669
+
1670
+ let dataCnts = 0;
1671
+ [spriteData[tmpFrame], dataCnts] = checkDuplicatedObjects(spriteData[tmpFrame]);
1672
+ spriteData[tmpFrame][dataCnts] = {
1673
+ depth: tmpSpriteData[1],
1674
+ styleData: getCssCustomProperty(tmpSpriteData[1], tmpSpriteData[2]),
1675
+ };
1676
+ });
1677
+ return [spriteData, 1];
1678
+ };
1679
+
1567
1680
  /**
1568
1681
  * 画像ファイルかどうかをチェック
1569
1682
  * @param {string} _str
@@ -1652,6 +1765,22 @@ const drawSpriteData = (_frame, _displayName, _depthName) => {
1652
1765
  const drawMainSpriteData = (_frame, _depthName) =>
1653
1766
  g_scoreObj[`${_depthName}Data`][_frame].forEach(tmpObj => drawBaseSpriteData(tmpObj, _depthName));
1654
1767
 
1768
+ /**
1769
+ * スタイル切替
1770
+ * @param {number} _frame
1771
+ * @param {string} _displayName
1772
+ */
1773
+ const drawStyleData = (_frame, _displayName) => {
1774
+ g_headerObj[`style${toCapitalize(_displayName)}Data`][_frame].forEach(tmpObj =>
1775
+ document.documentElement.style.setProperty(tmpObj.depth, tmpObj.styleData));
1776
+
1777
+ return _frame;
1778
+ };
1779
+
1780
+ const drawMainStyleData = (_frame) =>
1781
+ g_scoreObj.styleData[_frame].forEach(tmpObj =>
1782
+ document.documentElement.style.setProperty(tmpObj.depth, tmpObj.styleData));
1783
+
1655
1784
  /**
1656
1785
  * タイトル・リザルトモーションの描画
1657
1786
  * @param {string} _displayName
@@ -1660,7 +1789,7 @@ const drawTitleResultMotion = _displayName => {
1660
1789
  g_animationData.forEach(sprite => {
1661
1790
  const spriteName = `${sprite}${toCapitalize(_displayName)}`;
1662
1791
  if (g_headerObj[`${spriteName}Data`][g_scoreObj[`${spriteName}FrameNum`]] !== undefined) {
1663
- g_scoreObj[`${spriteName}FrameNum`] = drawSpriteData(g_scoreObj[`${spriteName}FrameNum`], _displayName, sprite);
1792
+ g_scoreObj[`${spriteName}FrameNum`] = g_animationFunc.draw[sprite](g_scoreObj[`${spriteName}FrameNum`], _displayName, sprite);
1664
1793
  }
1665
1794
  });
1666
1795
  };
@@ -1863,7 +1992,7 @@ const initialControl = async () => {
1863
1992
  await loadChartFile(0);
1864
1993
 
1865
1994
  // 共通設定ファイルの指定
1866
- let [settingType, settingRoot] = getFilePath(g_rootObj.settingType ?? ``, C_DIR_JS);
1995
+ let [settingType, settingRoot] = getFilePath(g_rootObj.settingType ?? ``);
1867
1996
  if (settingType !== ``) {
1868
1997
  settingType = `_${settingType}`;
1869
1998
  }
@@ -2462,6 +2591,26 @@ const calcLevel = _scoreObj => {
2462
2591
  };
2463
2592
  };
2464
2593
 
2594
+ /**
2595
+ * ロケールを含んだヘッダーの優先度設定
2596
+ * @param {object} _obj
2597
+ * @param {...any} _params
2598
+ */
2599
+ const getHeader = (_obj, ..._params) => {
2600
+ let headerLocale, headerDf;
2601
+ Object.keys(_params).forEach(j => {
2602
+ headerLocale ??= _obj[`${_params[j]}${g_localeObj.val}`];
2603
+ headerDf ??= _obj[_params[j]];
2604
+ });
2605
+ return headerLocale ?? headerDf;
2606
+ };
2607
+
2608
+ /**
2609
+ * ヘッダー名の互換設定
2610
+ * @param {string} _param
2611
+ */
2612
+ const getHname = _param => [_param, _param.toLowerCase()];
2613
+
2465
2614
  /**
2466
2615
  * 譜面ヘッダーの分解(スキン、jsファイルなどの設定)
2467
2616
  * @param {object} _dosObj
@@ -2495,11 +2644,11 @@ const preheaderConvert = _dosObj => {
2495
2644
  setJsFiles(tmpSkinTypes, C_DIR_SKIN, `skin`);
2496
2645
 
2497
2646
  // 外部jsファイルの指定
2498
- const tmpCustomjs = _dosObj.customjs ?? g_presetObj.customJs ?? C_JSF_CUSTOM;
2647
+ const tmpCustomjs = getHeader(_dosObj, ...getHname(`customJs`)) ?? g_presetObj.customJs ?? C_JSF_CUSTOM;
2499
2648
  setJsFiles(tmpCustomjs.replaceAll(`*`, g_presetObj.customJs).split(`,`), C_DIR_JS);
2500
2649
 
2501
2650
  // 外部cssファイルの指定
2502
- const tmpCustomcss = _dosObj.customcss ?? g_presetObj.customCss ?? ``;
2651
+ const tmpCustomcss = getHeader(_dosObj, ...getHname(`customCss`)) ?? g_presetObj.customCss ?? ``;
2503
2652
  setJsFiles(tmpCustomcss.replaceAll(`*`, g_presetObj.customCss).split(`,`), C_DIR_CSS);
2504
2653
 
2505
2654
  // デフォルト曲名表示、背景、Ready表示の利用有無
@@ -2524,12 +2673,18 @@ const headerConvert = _dosObj => {
2524
2673
  // ヘッダー群の格納先
2525
2674
  const obj = {};
2526
2675
 
2527
- /**
2528
- * ロケールを含んだヘッダーの取得
2529
- * @param {object} _obj
2530
- * @param {string} _param
2531
- */
2532
- const getHeader = (_obj, _param) => _obj[`${_param}${g_localeObj.val}`] ?? _obj[_param];
2676
+ // 自動プリロードの設定
2677
+ obj.autoPreload = setBoolVal(_dosObj.autoPreload, true);
2678
+ g_headerObj.autoPreload = obj.autoPreload;
2679
+
2680
+ // デフォルトスタイルのバックアップ
2681
+ getCssCustomProperties();
2682
+
2683
+ // 初期で変更するカスタムプロパティを設定
2684
+ Object.keys(_dosObj).filter(val => val.startsWith(`--`) && hasVal(_dosObj[val])).forEach(prop => {
2685
+ g_cssBkProperties[prop] = getCssCustomProperty(prop, _dosObj[prop]);
2686
+ document.documentElement.style.setProperty(prop, g_cssBkProperties[prop]);
2687
+ });
2533
2688
 
2534
2689
  // フォントの設定
2535
2690
  obj.customFont = _dosObj.customFont ?? ``;
@@ -2877,10 +3032,6 @@ const headerConvert = _dosObj => {
2877
3032
  // ハッシュタグ
2878
3033
  obj.hashTag = _dosObj.hashTag ?? ``;
2879
3034
 
2880
- // 自動プリロードの設定
2881
- obj.autoPreload = setBoolVal(_dosObj.autoPreload, true);
2882
- g_headerObj.autoPreload = obj.autoPreload;
2883
-
2884
3035
  // 読込対象の画像を指定(rel:preload)と同じ
2885
3036
  obj.preloadImages = [];
2886
3037
  if (hasVal(_dosObj.preloadImages)) {
@@ -2919,29 +3070,30 @@ const headerConvert = _dosObj => {
2919
3070
  obj.readyHtml = _dosObj.readyHtml ?? ``;
2920
3071
 
2921
3072
  // デフォルト曲名表示のフォントサイズ
2922
- obj.titlesize = getHeader(_dosObj, `titlesize`) ?? ``;
3073
+ obj.titlesize = getHeader(_dosObj, ...getHname(`titleSize`)) ?? ``;
2923
3074
 
2924
3075
  // デフォルト曲名表示のフォント名
2925
3076
  // (使用例: |titlefont=Century,Meiryo UI|)
2926
3077
  obj.titlefonts = g_titleLists.defaultFonts.concat();
2927
- getHeader(_dosObj, `titlefont`)?.split(`$`).forEach((font, j) => obj.titlefonts[j] = `'${(font.replaceAll(`,`, `', '`))}'`);
3078
+ getHeader(_dosObj, ...getHname(`titleFont`))?.split(`$`).forEach((font, j) => obj.titlefonts[j] = `'${(font.replaceAll(`,`, `', '`))}'`);
2928
3079
  if (obj.titlefonts[1] === undefined) {
2929
3080
  obj.titlefonts[1] = obj.titlefonts[0];
2930
3081
  }
2931
3082
 
2932
3083
  // デフォルト曲名表示, 背景矢印のグラデーション指定css
2933
- g_titleLists.grdList.forEach(_name => {
2934
- obj[`${_name}s`] = [];
2935
- if (hasVal(_dosObj[_name])) {
2936
- const tmpTitlegrd = _dosObj[_name].replaceAll(`,`, `:`);
2937
- obj[`${_name}s`] = tmpTitlegrd.split(`$`);
2938
- obj[`${_name}`] = obj[`${_name}s`][0] ?? ``;
3084
+ [`titlegrd`, `titleArrowgrd`].forEach(_name => {
3085
+ const objName = `${_name.toLowerCase()}`;
3086
+ obj[`${objName}s`] = [];
3087
+ const tmpTitlegrd = getHeader(_dosObj, ...getHname(_name))?.replaceAll(`,`, `:`);
3088
+ if (hasVal(tmpTitlegrd)) {
3089
+ obj[`${objName}s`] = tmpTitlegrd.split(`$`);
3090
+ obj[`${objName}`] = obj[`${objName}s`][0] ?? ``;
2939
3091
  }
2940
3092
  });
2941
3093
 
2942
3094
  // デフォルト曲名表示の表示位置調整
2943
3095
  obj.titlepos = [[0, 0], [0, 0]];
2944
- getHeader(_dosObj, `titlepos`)?.split(`$`).forEach((pos, j) => obj.titlepos[j] = pos.split(`,`).map(x => parseFloat(x)));
3096
+ getHeader(_dosObj, ...getHname(`titlePos`))?.split(`$`).forEach((pos, j) => obj.titlepos[j] = pos.split(`,`).map(x => parseFloat(x)));
2945
3097
 
2946
3098
  // タイトル文字のアニメーション設定
2947
3099
  obj.titleAnimationName = [`leftToRight`];
@@ -2950,14 +3102,14 @@ const headerConvert = _dosObj => {
2950
3102
  obj.titleAnimationTimingFunction = [`ease`];
2951
3103
  obj.titleAnimationClass = [``];
2952
3104
 
2953
- _dosObj.titleanimation?.split(`$`).forEach((pos, j) => {
3105
+ getHeader(_dosObj, ...getHname(`titleAnimation`))?.split(`$`).forEach((pos, j) => {
2954
3106
  const titleAnimation = pos.split(`,`);
2955
3107
  obj.titleAnimationName[j] = setVal(titleAnimation[0], obj.titleAnimationName[0]);
2956
3108
  obj.titleAnimationDuration[j] = setVal(titleAnimation[1] / g_fps, obj.titleAnimationDuration[0], C_TYP_FLOAT);
2957
3109
  obj.titleAnimationDelay[j] = setVal(titleAnimation[2] / g_fps, obj.titleAnimationDelay[0], C_TYP_FLOAT);
2958
3110
  obj.titleAnimationTimingFunction[j] = setVal(titleAnimation[3], obj.titleAnimationName[3]);
2959
3111
  });
2960
- _dosObj.titleanimationclass?.split(`$`).forEach((animationClass, j) =>
3112
+ getHeader(_dosObj, ...getHname(`titleAnimationClass`))?.split(`$`).forEach((animationClass, j) =>
2961
3113
  obj.titleAnimationClass[j] = animationClass ?? ``);
2962
3114
 
2963
3115
  if (obj.titleAnimationName.length === 1) {
@@ -2969,7 +3121,7 @@ const headerConvert = _dosObj => {
2969
3121
  }
2970
3122
 
2971
3123
  // デフォルト曲名表示の複数行時の縦間隔
2972
- obj.titlelineheight = setIntVal(getHeader(_dosObj, `titlelineheight`), ``);
3124
+ obj.titlelineheight = setIntVal(getHeader(_dosObj, ...getHname(`titleLineHeight`)), ``);
2973
3125
 
2974
3126
  // フリーズアローの始点で通常矢印の判定を行うか(dotさんソース方式)
2975
3127
  obj.frzStartjdgUse = setBoolVal(_dosObj.frzStartjdgUse ?? g_presetObj.frzStartjdgUse);
@@ -3045,13 +3197,12 @@ const headerConvert = _dosObj => {
3045
3197
  // [フレーム数,階層,背景パス,class(CSSで別定義),X,Y,width,height,opacity,animationName,animationDuration]
3046
3198
  g_animationData.forEach(sprite => {
3047
3199
  obj[`${sprite}TitleData`] = [];
3048
- obj[`${sprite}TitleData`].length = 0;
3049
3200
  obj[`${sprite}TitleMaxDepth`] = -1;
3050
3201
 
3051
3202
  const dataList = [_dosObj[`${sprite}title${g_localeObj.val}_data`], _dosObj[`${sprite}title_data`]];
3052
3203
  const data = dataList.find((v) => v !== undefined);
3053
3204
  if (hasVal(data)) {
3054
- [obj[`${sprite}TitleData`], obj[`${sprite}TitleMaxDepth`]] = makeSpriteData(data);
3205
+ [obj[`${sprite}TitleData`], obj[`${sprite}TitleMaxDepth`]] = g_animationFunc.make[sprite](data);
3055
3206
  }
3056
3207
  });
3057
3208
 
@@ -3068,7 +3219,7 @@ const headerConvert = _dosObj => {
3068
3219
  obj.resultMotionSet = setBoolVal(_dosObj.resultMotionSet, true);
3069
3220
 
3070
3221
  // 譜面明細の使用可否
3071
- const tmpDetails = _dosObj.scoreDetailUse?.split(`,`).filter(val => hasVal(val) && val !== `false`)
3222
+ const tmpDetails = getHeader(_dosObj, `scoreDetailUse`, `chartDetailUse`)?.split(`,`).filter(val => hasVal(val) && val !== `false`)
3072
3223
  .map(val => replaceStr(val, g_settings.scoreDetailTrans));
3073
3224
  g_settings.scoreDetails = g_settings.scoreDetailDefs.filter(val => tmpDetails?.includes(val) || tmpDetails === undefined);
3074
3225
 
@@ -3581,14 +3732,13 @@ const keysConvert = (_dosObj, { keyExtraList = _dosObj.keyExtraList?.split(`,`)
3581
3732
  const dfPtn = setIntVal(g_keyObj.dfPtnNum);
3582
3733
 
3583
3734
  if (hasVal(_dosObj[keyheader])) {
3584
- const tmpArray = splitLF2(_dosObj[keyheader]);
3585
- for (let k = 0; k < tmpArray.length; k++) {
3586
- if (existParam(tmpArray[k], `${keyheader}_${k + dfPtn}`)) {
3587
- continue;
3735
+ splitLF2(_dosObj[keyheader])?.forEach((tmpParam, k) => {
3736
+ if (existParam(tmpParam, `${keyheader}_${k + dfPtn}`)) {
3737
+ return;
3588
3738
  }
3589
3739
 
3590
3740
  let ptnCnt = 0;
3591
- tmpArray[k].split(`/`).forEach(list => {
3741
+ tmpParam.split(`/`).forEach(list => {
3592
3742
 
3593
3743
  const keyPtn = getKeyPtnName(list);
3594
3744
  if (list === ``) {
@@ -3617,7 +3767,7 @@ const keysConvert = (_dosObj, { keyExtraList = _dosObj.keyExtraList?.split(`,`)
3617
3767
  }
3618
3768
  });
3619
3769
  g_keyObj[`${keyheader}_${k + dfPtn}`] = structuredClone(g_keyObj[`${keyheader}_${k + dfPtn}_0`]);
3620
- }
3770
+ });
3621
3771
 
3622
3772
  } else if (g_keyObj[`${keyheader}_${dfPtn}_0`] === undefined) {
3623
3773
  // 特に指定が無い場合はkeyCtrlX_Yの配列長で決定
@@ -3663,39 +3813,36 @@ const keysConvert = (_dosObj, { keyExtraList = _dosObj.keyExtraList?.split(`,`)
3663
3813
  const keyheader = _name + _key;
3664
3814
  const dfPtn = setIntVal(g_keyObj.dfPtnNum);
3665
3815
 
3666
- if (_dosObj[keyheader] !== undefined) {
3667
- const tmpParams = splitLF2(_dosObj[keyheader]);
3668
- for (let k = 0; k < tmpParams.length; k++) {
3669
- const pairName = `${_pairName}${_key}_${k + dfPtn}`;
3670
- if (!hasVal(tmpParams[k])) {
3671
- continue;
3672
- }
3673
- g_keyObj[pairName] = {}
3816
+ splitLF2(_dosObj[keyheader])?.forEach((tmpParam, k) => {
3817
+ const pairName = `${_pairName}${_key}_${k + dfPtn}`;
3818
+ if (!hasVal(tmpParam)) {
3819
+ return;
3820
+ }
3821
+ g_keyObj[pairName] = {}
3674
3822
 
3675
- // デフォルト項目がある場合は先に定義
3676
- if (_defaultName !== ``) {
3677
- g_keyObj[pairName][_defaultName] = [...Array(g_keyObj[`${g_keyObj.defaultProp}${_key}_${k + dfPtn}`].length)].fill(_defaultVal);
3678
- }
3679
- tmpParams[k].split(`/`).forEach(pairs => {
3680
- const keyPtn = getKeyPtnName(pairs);
3681
- if (pairs === ``) {
3682
- } else if (g_keyObj[`${_pairName}${keyPtn}`] !== undefined) {
3683
- // 他のキーパターン指定時、該当があればプロパティを全コピー
3684
- Object.assign(g_keyObj[pairName], g_keyObj[`${_pairName}${keyPtn}`]);
3685
- } else {
3686
- // 通常の指定方法(例:|scroll8i=Cross::1,1,1,-,-,-,1,1/Split::1,1,1,1,-,-,-,-|)から取り込み
3687
- // 部分的にキーパターン指定があった場合は既存パターンを展開 (例: |scroll9j=Cross::1,7_0,1|)
3688
- const tmpParamPair = pairs.split(`::`);
3689
- g_keyObj[pairName][tmpParamPair[0]] =
3690
- makeBaseArray(tmpParamPair[1].split(`,`).map(n =>
3691
- g_keyObj[`${_pairName}${getKeyPtnName(n)}`] !== undefined ?
3692
- structuredClone(g_keyObj[`${_pairName}${getKeyPtnName(n)}`][tmpParamPair[0]]) :
3693
- [n === `-` ? -1 : parseInt(n, 10)]
3694
- ).flat(), g_keyObj[`${g_keyObj.defaultProp}${_key}_${k + dfPtn}`].length, _defaultVal);
3695
- }
3696
- });
3823
+ // デフォルト項目がある場合は先に定義
3824
+ if (_defaultName !== ``) {
3825
+ g_keyObj[pairName][_defaultName] = [...Array(g_keyObj[`${g_keyObj.defaultProp}${_key}_${k + dfPtn}`].length)].fill(_defaultVal);
3697
3826
  }
3698
- }
3827
+ tmpParam.split(`/`).forEach(pairs => {
3828
+ const keyPtn = getKeyPtnName(pairs);
3829
+ if (pairs === ``) {
3830
+ } else if (g_keyObj[`${_pairName}${keyPtn}`] !== undefined) {
3831
+ // 他のキーパターン指定時、該当があればプロパティを全コピー
3832
+ Object.assign(g_keyObj[pairName], g_keyObj[`${_pairName}${keyPtn}`]);
3833
+ } else {
3834
+ // 通常の指定方法(例:|scroll8i=Cross::1,1,1,-,-,-,1,1/Split::1,1,1,1,-,-,-,-|)から取り込み
3835
+ // 部分的にキーパターン指定があった場合は既存パターンを展開 (例: |scroll9j=Cross::1,7_0,1|)
3836
+ const tmpParamPair = pairs.split(`::`);
3837
+ g_keyObj[pairName][tmpParamPair[0]] =
3838
+ makeBaseArray(tmpParamPair[1]?.split(`,`).map(n =>
3839
+ g_keyObj[`${_pairName}${getKeyPtnName(n)}`] !== undefined ?
3840
+ structuredClone(g_keyObj[`${_pairName}${getKeyPtnName(n)}`][tmpParamPair[0]]) :
3841
+ [n === `-` ? -1 : parseInt(n, 10)]
3842
+ ).flat(), g_keyObj[`${g_keyObj.defaultProp}${_key}_${k + dfPtn}`].length, _defaultVal);
3843
+ }
3844
+ });
3845
+ });
3699
3846
  };
3700
3847
 
3701
3848
  // 対象キー毎に処理
@@ -3828,12 +3975,12 @@ const titleInit = _ => {
3828
3975
 
3829
3976
  // タイトル用フレーム初期化
3830
3977
  g_scoreObj.titleFrameNum = 0;
3831
- g_scoreObj.backTitleFrameNum = 0;
3832
- g_scoreObj.maskTitleFrameNum = 0;
3833
3978
 
3834
- // タイトル用ループカウンター
3835
- g_scoreObj.backTitleLoopCount = 0;
3836
- g_scoreObj.maskTitleLoopCount = 0;
3979
+ // タイトルアニメーション用フレーム初期化、ループカウンター設定
3980
+ g_animationData.forEach(sprite => {
3981
+ g_scoreObj[`${sprite}TitleFrameNum`] = 0;
3982
+ g_scoreObj[`${sprite}TitleLoopCount`] = 0;
3983
+ });
3837
3984
 
3838
3985
  const keyCtrlPtn = `${g_keyObj.currentKey}_${g_keyObj.currentPtn}`;
3839
3986
 
@@ -4077,15 +4224,14 @@ const titleInit = _ => {
4077
4224
  // ユーザカスタムイベント(フレーム毎)
4078
4225
  g_customJsObj.titleEnterFrame.forEach(func => func());
4079
4226
 
4080
- // 背景・マスクモーション
4227
+ // 背景・マスクモーション、スキン変更
4081
4228
  drawTitleResultMotion(g_currentPage);
4082
4229
 
4083
4230
  thisTime = performance.now();
4084
4231
  buffTime = thisTime - titleStartTime - g_scoreObj.titleFrameNum * 1000 / g_fps;
4085
4232
 
4086
4233
  g_scoreObj.titleFrameNum++;
4087
- g_scoreObj.backTitleFrameNum++;
4088
- g_scoreObj.maskTitleFrameNum++;
4234
+ g_animationData.forEach(sprite => g_scoreObj[`${sprite}TitleFrameNum`]++);
4089
4235
  g_timeoutEvtTitleId = setTimeout(flowTitleTimeline, 1000 / g_fps - buffTime);
4090
4236
  };
4091
4237
 
@@ -4491,6 +4637,7 @@ const drawSpeedGraph = _scoreId => {
4491
4637
 
4492
4638
  const avgX = [0, 0];
4493
4639
  const avgSubX = [1, 1];
4640
+ const lineX = [125, 210];
4494
4641
  Object.keys(speedObj).forEach((speedType, j) => {
4495
4642
  context.beginPath();
4496
4643
  let preY;
@@ -4519,13 +4666,12 @@ const drawSpeedGraph = _scoreId => {
4519
4666
  context.strokeStyle = speedObj[speedType].strokeColor;
4520
4667
  context.stroke();
4521
4668
 
4522
- const lineX = (speedType === `speed`) ? 125 : 210;
4523
4669
  context.beginPath();
4524
- context.moveTo(lineX, 215);
4525
- context.lineTo(lineX + 25, 215);
4670
+ context.moveTo(lineX[j], 215);
4671
+ context.lineTo(lineX[j] + 25, 215);
4526
4672
  context.stroke();
4527
4673
  context.font = `${g_limitObj.difSelectorSiz}px ${getBasicFont()}`;
4528
- context.fillText(g_lblNameObj[`s_${speedType}`], lineX + 30, 218);
4674
+ context.fillText(g_lblNameObj[`s_${speedType}`], lineX[j] + 30, 218);
4529
4675
 
4530
4676
  updateScoreDetailLabel(`Speed`, `${speedType}S`, speedObj[speedType].cnt, j, g_lblNameObj[`s_${speedType}`]);
4531
4677
  updateScoreDetailLabel(`Speed`, `avgD${speedType}`, avgSubX[j] === 1 ? `----` : `${(avgSubX[j]).toFixed(2)}x`, j + 4, g_lblNameObj[`s_avgD${speedType}`]);
@@ -7229,22 +7375,8 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
7229
7375
  * 矢印データの格納
7230
7376
  * @param {string} _data
7231
7377
  */
7232
- const storeArrowData = _data => {
7233
- let arrowData = [];
7234
-
7235
- if (hasVal(_data)) {
7236
- const tmpData = splitLF(_data).join(``);
7237
- if (tmpData !== undefined) {
7238
- arrowData = tmpData.split(`,`);
7239
- if (isNaN(parseFloat(arrowData[0]))) {
7240
- return [];
7241
- } else {
7242
- arrowData = arrowData.map(data => calcFrame(data)).sort((_a, _b) => _a - _b);
7243
- }
7244
- }
7245
- }
7246
- return arrowData;
7247
- };
7378
+ const storeArrowData = _data => hasVal(_data) ?
7379
+ splitLF(_data)?.join(``).split(`,`).filter(data => !isNaN(parseFloat(data))).map(data => calcFrame(data)).sort((_a, _b) => _a - _b) : [];
7248
7380
 
7249
7381
  for (let j = 0; j < keyNum; j++) {
7250
7382
 
@@ -7426,7 +7558,11 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
7426
7558
  /**
7427
7559
  * 歌詞表示、背景・マスクデータの優先順取得
7428
7560
  */
7429
- const getPriorityHeader = _ => {
7561
+ const getPriorityHeader = (_defaultHeaders = []) => {
7562
+ if (_defaultHeaders.length > 0) {
7563
+ return makeDedupliArray(_defaultHeaders);
7564
+ }
7565
+
7430
7566
  const list = [];
7431
7567
  const anotherKeyFlg = hasVal(g_keyObj[`transKey${_keyCtrlPtn}`]);
7432
7568
  let type = ``;
@@ -7513,17 +7649,17 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
7513
7649
  wordMaxDepth = tmpWordData[k + 1];
7514
7650
  }
7515
7651
 
7516
- let addFrame = 0;
7517
- [wordData[tmpWordData[k]], addFrame] =
7652
+ let dataCnts = 0;
7653
+ [wordData[tmpWordData[k]], dataCnts] =
7518
7654
  checkDuplicatedObjects(wordData[tmpWordData[k]]);
7519
7655
 
7520
7656
  if (tmpWordData.length > 3 && tmpWordData.length < 6) {
7521
7657
  tmpWordData[3] = setIntVal(tmpWordData[3], C_WOD_FRAME);
7522
- wordData[tmpWordData[0]][addFrame].push(tmpWordData[1],
7658
+ wordData[tmpWordData[0]][dataCnts].push(tmpWordData[1],
7523
7659
  escapeHtmlForEnabledTag(tmpWordData[2]), tmpWordData[3]);
7524
7660
  break;
7525
7661
  } else {
7526
- wordData[tmpWordData[k]][addFrame].push(tmpWordData[k + 1],
7662
+ wordData[tmpWordData[k]][dataCnts].push(tmpWordData[k + 1],
7527
7663
  escapeHtmlForEnabledTag(tmpWordData[k + 2] ?? ``));
7528
7664
  }
7529
7665
  }
@@ -7533,36 +7669,19 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
7533
7669
  };
7534
7670
 
7535
7671
  /**
7536
- * 背景・マスクデータの分解
7672
+ * 背景・マスク、スキン変更データの分解
7537
7673
  * @param {string} _header
7538
- * @param {string} _scoreNo
7539
- */
7540
- const makeBackgroundData = (_header, _scoreNo) => {
7541
- const dataList = [];
7542
- const addDataList = (_type = ``) => dataList.push(...getPriorityList(_header, _type, _scoreNo));
7543
- getPriorityHeader().forEach(val => addDataList(val));
7544
-
7545
- const data = dataList.find((v) => v !== undefined);
7546
- return (data !== undefined ? makeSpriteData(data, calcFrame) : [[], -1]);
7547
- };
7548
-
7549
- /**
7550
- * リザルトモーションデータ(結果画面用背景・マスクデータ)の分解
7551
- * @param {string} _header 背景、マスク (back, mask)
7552
- * @param {string} _resultType リザルトモーションの種類 (result, failedB, failedS)
7553
7674
  * @param {string} _scoreNo 譜面番号
7554
- * @param {string} _defaultType _resultTypeが無いときの代替名
7675
+ * @param {array} resultTypes リザルトモーションの種類 (result, failedB, failedS)
7555
7676
  */
7556
- const makeBackgroundResultData = (_header, _resultType, _scoreNo, _defaultType = ``) => {
7677
+ const makeBackgroundData = (_header, _scoreNo, { resultTypes = [] } = {}) => {
7557
7678
  const dataList = [];
7679
+ const calcFrameFunc = resultTypes.length > 0 ? calcFrame : undefined;
7558
7680
  const addDataList = (_type = ``) => dataList.push(...getPriorityList(_header, _type, _scoreNo));
7559
- addDataList(_resultType);
7560
- if (_defaultType !== ``) {
7561
- addDataList(_defaultType);
7562
- }
7681
+ getPriorityHeader(resultTypes).forEach(val => addDataList(val));
7563
7682
 
7564
7683
  const data = dataList.find((v) => v !== undefined);
7565
- return (data !== undefined ? makeSpriteData(data) : [[], -1]);
7684
+ return (data !== undefined ? g_animationFunc.make[_header](data, calcFrameFunc) : [[], -1]);
7566
7685
  };
7567
7686
 
7568
7687
  // 速度変化データの分解 (2つで1セット)
@@ -7612,34 +7731,30 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
7612
7731
  [obj.wordData, obj.wordMaxDepth] = makeWordData(scoreIdHeader);
7613
7732
  }
7614
7733
 
7615
- // 背景・マスクデータの分解 (下記すべてで1セット、改行区切り)
7616
- // [フレーム数, 階層, 背景パス, class(CSSで別定義), X, Y, width, height, opacity, animationName, animationDuration]
7617
- obj.maskData = [];
7618
- obj.maskMaxDepth = -1;
7619
- obj.backData = [];
7620
- obj.backMaxDepth = -1;
7621
- if (g_stateObj.d_background === C_FLG_OFF) {
7622
- } else {
7623
- g_animationData.forEach(sprite =>
7624
- [obj[`${sprite}Data`], obj[`${sprite}MaxDepth`]] = makeBackgroundData(sprite, scoreIdHeader));
7625
- }
7734
+ // 背景・マスク・スキン変更データの分解 (下記すべてで1セット、改行区切り)
7735
+ // - 背景・マスク: [フレーム数, 階層, 背景パス, class(CSSで別定義), X, Y, width, height, opacity, animationName, animationDuration, animationFillMode]
7736
+ // - スキン変更 : [フレーム数, CSSカスタムプロパティ名, 設定内容]
7737
+ g_animationData.forEach(sprite => {
7738
+ obj[`${sprite}Data`] = [];
7739
+ obj[`${sprite}MaxDepth`] = -1;
7626
7740
 
7627
- // 結果画面用・背景/マスクデータの分解 (下記すべてで1セット、改行区切り)
7628
- // [フレーム数,階層,背景パス,class(CSSで別定義),X,Y,width,height,opacity,animationName,animationDuration]
7629
- if (g_stateObj.d_background === C_FLG_OFF && g_headerObj.resultMotionSet) {
7630
- const backgroundResults = [`backResult`, `maskResult`, `backFailed`, `maskFailed`];
7631
- backgroundResults.forEach(backName => {
7632
- g_headerObj[`${backName}Data`] = [];
7633
- g_headerObj[`${backName}MaxDepth`] = -1;
7634
- });
7635
- } else {
7636
- g_animationData.forEach(sprite => {
7741
+ if (g_stateObj.d_background === C_FLG_OFF) {
7742
+ } else {
7743
+ [obj[`${sprite}Data`], obj[`${sprite}MaxDepth`]] = makeBackgroundData(sprite, scoreIdHeader);
7744
+ }
7745
+
7746
+ if (g_stateObj.d_background === C_FLG_OFF && g_headerObj.resultMotionSet) {
7747
+ [`Result`, `Failed`].forEach(backName => {
7748
+ g_headerObj[`${backName}Data`] = [];
7749
+ g_headerObj[`${backName}MaxDepth`] = -1;
7750
+ });
7751
+ } else {
7637
7752
  [g_headerObj[`${sprite}ResultData`], g_headerObj[`${sprite}ResultMaxDepth`]] =
7638
- makeBackgroundResultData(sprite, `result`, scoreIdHeader);
7753
+ makeBackgroundData(sprite, scoreIdHeader, { resultTypes: [`result`] });
7639
7754
  [g_headerObj[`${sprite}FailedData`], g_headerObj[`${sprite}FailedMaxDepth`]] =
7640
- makeBackgroundResultData(sprite, `failed${g_stateObj.lifeMode.slice(0, 1)}`, scoreIdHeader, `result`);
7641
- });
7642
- }
7755
+ makeBackgroundData(sprite, scoreIdHeader, { resultTypes: [`failed${g_stateObj.lifeMode.slice(0, 1)}`, `result`] });
7756
+ }
7757
+ });
7643
7758
 
7644
7759
  // キー変化定義
7645
7760
  obj.keychFrames = [0];
@@ -8041,11 +8156,11 @@ const pushArrows = (_dataObj, _speedOnFrame, _motionOnFrame, _firstArrivalFrame)
8041
8156
  (cgArrays.includes(_type) ? _data[startNum][_j][0] === _data[startNum][_k][0] :
8042
8157
  _data[startNum][_j].depth === _data[startNum][_k].depth);
8043
8158
 
8044
- const fuzzyCheck = (_str, _list) => listMatching(_str, _list);
8045
8159
  const isExceptData = {
8046
- word: (_exceptList, _j) => fuzzyCheck(_data[startNum][_j][1], _exceptList.word),
8047
- back: (_exceptList, _j) => fuzzyCheck(_data[startNum][_j].animationName, _exceptList.back),
8048
- mask: (_exceptList, _j) => fuzzyCheck(_data[startNum][_j].animationName, _exceptList.mask),
8160
+ word: (_exceptList, _j) => listMatching(_data[startNum][_j][1], _exceptList.word),
8161
+ back: (_exceptList, _j) => listMatching(_data[startNum][_j].animationName, _exceptList.back),
8162
+ mask: (_exceptList, _j) => listMatching(_data[startNum][_j].animationName, _exceptList.mask),
8163
+ style: (_exceptList, _j) => listMatching(_data[startNum][_j].depth, _exceptList.style),
8049
8164
  };
8050
8165
 
8051
8166
  const getLength = _list =>
@@ -8104,7 +8219,6 @@ const pushArrows = (_dataObj, _speedOnFrame, _motionOnFrame, _firstArrivalFrame)
8104
8219
 
8105
8220
  // 実際に処理させる途中変速配列を作成
8106
8221
  g_workObj.speedData = [];
8107
- g_workObj.speedData.length = 0;
8108
8222
  g_workObj.speedData.push(g_scoreObj.frameNum);
8109
8223
  g_workObj.speedData.push(_speedOnFrame[g_scoreObj.frameNum]);
8110
8224
 
@@ -8610,11 +8724,11 @@ const mainInit = _ => {
8610
8724
  // カラー・モーションを適用するオブジェクトの種類
8611
8725
  const objList = (g_stateObj.dummyId === `` ? [``] : [`dummy`, ``]);
8612
8726
 
8613
- // 背景・マスクモーション(0フレーム指定)
8727
+ // 背景・マスクモーション、スキン変更(0フレーム指定)
8614
8728
  if (g_scoreObj.frameNum === 0) {
8615
8729
  g_animationData.forEach(sprite => {
8616
8730
  if (g_scoreObj[`${sprite}Data`][0] !== undefined) {
8617
- drawMainSpriteData(0, sprite);
8731
+ g_animationFunc.drawMain[sprite](0, sprite);
8618
8732
  g_scoreObj[`${sprite}Data`][0] = undefined;
8619
8733
  }
8620
8734
  });
@@ -8914,7 +9028,7 @@ const mainInit = _ => {
8914
9028
  x: jdgX[j] + 170, y: jdgY[j],
8915
9029
  w: g_limitObj.jdgCharaWidth, h: g_limitObj.jdgCharaHeight, siz: g_limitObj.jdgCharaSiz,
8916
9030
  opacity: g_stateObj.opacity / 100, display: g_workObj.judgmentDisp,
8917
- }, g_cssObj[`common_${jdgCombos[j]}`]),
9031
+ }, g_cssObj[`common_combo${jdg}`]),
8918
9032
 
8919
9033
  // Fast/Slow表示
8920
9034
  createDivCss2Label(`diff${jdg}`, ``, {
@@ -9592,10 +9706,10 @@ const mainInit = _ => {
9592
9706
  }
9593
9707
  }
9594
9708
 
9595
- // 背景・マスクモーション
9709
+ // 背景・マスクモーション、スキン変更
9596
9710
  g_animationData.forEach(sprite => {
9597
9711
  if (g_scoreObj[`${sprite}Data`][currentFrame] !== undefined) {
9598
- drawMainSpriteData(currentFrame, sprite);
9712
+ g_animationFunc.drawMain[sprite](currentFrame, sprite);
9599
9713
  }
9600
9714
  });
9601
9715
 
@@ -10145,14 +10259,14 @@ const displayDiff = (_difFrame, _fjdg = ``, _justFrames = g_headerObj.justFrames
10145
10259
  g_workObj.diffList.push(_difFrame);
10146
10260
  const difCnt = Math.abs(_difFrame);
10147
10261
  if (_difFrame > g_judgObj.arrowJ[g_judgPosObj.shobon]) {
10148
- diffJDisp = `<span class="common_kita">Excessive</span>`;
10262
+ diffJDisp = `<span class="common_excessive">Excessive</span>`;
10149
10263
  g_resultObj.excessive++;
10150
10264
  lifeDamage(true);
10151
10265
  } else if (_difFrame > _justFrames) {
10152
- diffJDisp = `<span class="common_matari">Fast ${difCnt} Frames</span>`;
10266
+ diffJDisp = `<span class="common_diffFast">Fast ${difCnt} Frames</span>`;
10153
10267
  g_resultObj.fast++;
10154
10268
  } else if (_difFrame < _justFrames * (-1)) {
10155
- diffJDisp = `<span class="common_shobon">Slow ${difCnt} Frames</span>`;
10269
+ diffJDisp = `<span class="common_diffSlow">Slow ${difCnt} Frames</span>`;
10156
10270
  g_resultObj.slow++;
10157
10271
  }
10158
10272
  document.getElementById(`diff${_fjdg}J`).innerHTML = diffJDisp;
@@ -10378,12 +10492,12 @@ const resultInit = _ => {
10378
10492
 
10379
10493
  // 結果画面用フレーム初期化
10380
10494
  g_scoreObj.resultFrameNum = 0;
10381
- g_scoreObj.backResultFrameNum = 0;
10382
- g_scoreObj.maskResultFrameNum = 0;
10383
10495
 
10384
- // 結果画面用ループカウンター
10385
- g_scoreObj.backResultLoopCount = 0;
10386
- g_scoreObj.maskResultLoopCount = 0;
10496
+ // リザルトアニメーション用フレーム初期化、ループカウンター設定
10497
+ g_animationData.forEach(sprite => {
10498
+ g_scoreObj[`${sprite}ResultFrameNum`] = 0;
10499
+ g_scoreObj[`${sprite}ResultLoopCount`] = 0;
10500
+ });
10387
10501
 
10388
10502
  const divRoot = document.querySelector(`#divRoot`);
10389
10503
 
@@ -10401,14 +10515,14 @@ const resultInit = _ => {
10401
10515
  g_animationData.forEach(sprite => {
10402
10516
  const failedData = g_rootObj[`${sprite}failedS${scoreIdHeader}_data`] ?? g_rootObj[`${sprite}failedS_data`];
10403
10517
  if (failedData !== undefined) {
10404
- [g_headerObj[`${sprite}ResultData`], g_headerObj[`${sprite}ResultMaxDepth`]] = makeSpriteData(failedData);
10518
+ [g_headerObj[`${sprite}ResultData`], g_headerObj[`${sprite}ResultMaxDepth`]] = g_animationFunc.make[sprite](failedData);
10405
10519
  }
10406
10520
  });
10407
10521
  } else if (g_gameOverFlg) {
10408
- g_headerObj.backResultData = g_headerObj.backFailedData.concat();
10409
- g_headerObj.maskResultData = g_headerObj.maskFailedData.concat();
10410
- g_headerObj.backResultMaxDepth = g_headerObj.backFailedMaxDepth;
10411
- g_headerObj.maskResultMaxDepth = g_headerObj.maskFailedMaxDepth;
10522
+ g_animationData.forEach(sprite => {
10523
+ g_headerObj[`${sprite}ResultData`] = g_headerObj[`${sprite}FailedData`].concat();
10524
+ g_headerObj[`${sprite}ResultMaxDepth`] = g_headerObj[`${sprite}FailedMaxDepth`];
10525
+ });
10412
10526
  }
10413
10527
  }
10414
10528
 
@@ -10567,20 +10681,20 @@ const resultInit = _ => {
10567
10681
  ));
10568
10682
  if (g_stateObj.autoAll === C_FLG_OFF) {
10569
10683
  multiAppend(resultWindow,
10570
- makeCssResultSymbol(`lblFast`, 350, g_cssObj.common_matari, 0, g_lblNameObj.j_fast),
10571
- makeCssResultSymbol(`lblSlow`, 350, g_cssObj.common_shobon, 2, g_lblNameObj.j_slow),
10684
+ makeCssResultSymbol(`lblFast`, 350, g_cssObj.common_diffFast, 0, g_lblNameObj.j_fast),
10685
+ makeCssResultSymbol(`lblSlow`, 350, g_cssObj.common_diffSlow, 2, g_lblNameObj.j_slow),
10572
10686
  makeCssResultSymbol(`lblFastS`, 260, g_cssObj.score, 1, g_resultObj.fast, C_ALIGN_RIGHT),
10573
10687
  makeCssResultSymbol(`lblSlowS`, 260, g_cssObj.score, 3, g_resultObj.slow, C_ALIGN_RIGHT),
10574
10688
  );
10575
10689
  if (estimatedAdj !== ``) {
10576
10690
  multiAppend(resultWindow,
10577
- makeCssResultSymbol(`lblAdj`, 350, g_cssObj.common_shakin, 4, g_lblNameObj.j_adj),
10691
+ makeCssResultSymbol(`lblAdj`, 350, g_cssObj.common_estAdj, 4, g_lblNameObj.j_adj),
10578
10692
  makeCssResultSymbol(`lblAdjS`, 260, g_cssObj.score, 5, `${getDiffFrame(estimatedAdj)}`, C_ALIGN_RIGHT),
10579
10693
  );
10580
10694
  }
10581
10695
  if (g_stateObj.excessive === C_FLG_ON) {
10582
10696
  multiAppend(resultWindow,
10583
- makeCssResultSymbol(`lblExcessive`, 350, g_cssObj.common_kita, 6, g_lblNameObj.j_excessive),
10697
+ makeCssResultSymbol(`lblExcessive`, 350, g_cssObj.common_excessive, 6, g_lblNameObj.j_excessive),
10584
10698
  makeCssResultSymbol(`lblExcessiveS`, 260, g_cssObj.score, 7, g_resultObj.excessive, C_ALIGN_RIGHT),
10585
10699
  );
10586
10700
  }
@@ -10783,7 +10897,7 @@ const resultInit = _ => {
10783
10897
  g_animationData.forEach(sprite => {
10784
10898
  if (g_scoreObj[`${sprite}ResultFrameNum`] === 0) {
10785
10899
  if (g_headerObj[`${sprite}ResultData`][0] !== undefined) {
10786
- g_scoreObj[`${sprite}ResultFrameNum`] = drawSpriteData(0, `result`, sprite);
10900
+ g_scoreObj[`${sprite}ResultFrameNum`] = g_animationFunc.draw[sprite](0, `result`, sprite);
10787
10901
  g_headerObj[`${sprite}ResultData`][0] = undefined;
10788
10902
  }
10789
10903
  }
@@ -10797,7 +10911,7 @@ const resultInit = _ => {
10797
10911
  // ユーザカスタムイベント(フレーム毎)
10798
10912
  g_customJsObj.resultEnterFrame.forEach(func => func());
10799
10913
 
10800
- // 背景・マスクモーション
10914
+ // 背景・マスクモーション、スキン変更
10801
10915
  drawTitleResultMotion(g_currentPage);
10802
10916
 
10803
10917
  // リザルト画面移行後のフェードアウト処理
@@ -10820,8 +10934,7 @@ const resultInit = _ => {
10820
10934
  buffTime = thisTime - resultStartTime - g_scoreObj.resultFrameNum * 1000 / g_fps;
10821
10935
 
10822
10936
  g_scoreObj.resultFrameNum++;
10823
- g_scoreObj.backResultFrameNum++;
10824
- g_scoreObj.maskResultFrameNum++;
10937
+ g_animationData.forEach(sprite => g_scoreObj[`${sprite}ResultFrameNum`]++);
10825
10938
  g_timeoutEvtResultId = setTimeout(flowResultTimeline, 1000 / g_fps - buffTime);
10826
10939
  };
10827
10940
  flowResultTimeline();