danoniplus 46.4.0 → 46.5.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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Dancing☆Onigiri (CW Edition)
2
2
 
3
3
  [![CodeQL](https://github.com/cwtickle/danoniplus/workflows/CodeQL/badge.svg)](https://github.com/cwtickle/danoniplus/actions?query=workflow%3ACodeQL)
4
- ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/cwtickle/danoniplus?utm_source=oss&utm_medium=github&utm_campaign=cwtickle%2Fdanoniplus&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews&logo=coderabbit)
4
+ [![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/cwtickle/danoniplus?utm_source=oss&utm_medium=github&utm_campaign=cwtickle%2Fdanoniplus&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews&logo=coderabbit)](https://www.coderabbit.ai/)
5
5
  [![Discord](https://img.shields.io/discord/698460971231870977?style=flat&logo=discord&logoColor=%23ffffff&label=Discord&labelColor=%236666ff&color=%23000066)](https://discord.gg/YVWUdUGyMy)
6
6
  [![DeepWiki](https://img.shields.io/badge/DeepWiki-cwtickle%2Fdanoniplus-blue.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAyCAYAAAAnWDnqAAAAAXNSR0IArs4c6QAAA05JREFUaEPtmUtyEzEQhtWTQyQLHNak2AB7ZnyXZMEjXMGeK/AIi+QuHrMnbChYY7MIh8g01fJoopFb0uhhEqqcbWTp06/uv1saEDv4O3n3dV60RfP947Mm9/SQc0ICFQgzfc4CYZoTPAswgSJCCUJUnAAoRHOAUOcATwbmVLWdGoH//PB8mnKqScAhsD0kYP3j/Yt5LPQe2KvcXmGvRHcDnpxfL2zOYJ1mFwrryWTz0advv1Ut4CJgf5uhDuDj5eUcAUoahrdY/56ebRWeraTjMt/00Sh3UDtjgHtQNHwcRGOC98BJEAEymycmYcWwOprTgcB6VZ5JK5TAJ+fXGLBm3FDAmn6oPPjR4rKCAoJCal2eAiQp2x0vxTPB3ALO2CRkwmDy5WohzBDwSEFKRwPbknEggCPB/imwrycgxX2NzoMCHhPkDwqYMr9tRcP5qNrMZHkVnOjRMWwLCcr8ohBVb1OMjxLwGCvjTikrsBOiA6fNyCrm8V1rP93iVPpwaE+gO0SsWmPiXB+jikdf6SizrT5qKasx5j8ABbHpFTx+vFXp9EnYQmLx02h1QTTrl6eDqxLnGjporxl3NL3agEvXdT0WmEost648sQOYAeJS9Q7bfUVoMGnjo4AZdUMQku50McDcMWcBPvr0SzbTAFDfvJqwLzgxwATnCgnp4wDl6Aa+Ax283gghmj+vj7feE2KBBRMW3FzOpLOADl0Isb5587h/U4gGvkt5v60Z1VLG8BhYjbzRwyQZemwAd6cCR5/XFWLYZRIMpX39AR0tjaGGiGzLVyhse5C9RKC6ai42ppWPKiBagOvaYk8lO7DajerabOZP46Lby5wKjw1HCRx7p9sVMOWGzb/vA1hwiWc6jm3MvQDTogQkiqIhJV0nBQBTU+3okKCFDy9WwferkHjtxib7t3xIUQtHxnIwtx4mpg26/HfwVNVDb4oI9RHmx5WGelRVlrtiw43zboCLaxv46AZeB3IlTkwouebTr1y2NjSpHz68WNFjHvupy3q8TFn3Hos2IAk4Ju5dCo8B3wP7VPr/FGaKiG+T+v+TQqIrOqMTL1VdWV1DdmcbO8KXBz6esmYWYKPwDL5b5FA1a0hwapHiom0r/cKaoqr+27/XcrS5UwSMbQAAAABJRU5ErkJggg==)](https://deepwiki.com/cwtickle/danoniplus)
7
7
  [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/cwtickle/danoniplus?sort=semver)](https://github.com/cwtickle/danoniplus/security/policy)
@@ -120,7 +120,7 @@ If you would like to cooperate with the development, please see below. Even if y
120
120
 
121
121
  ### Dancing☆Onigiri
122
122
 
123
- - [Dancing☆Onigiri Preview](https://danonicw.skr.jp/)
123
+ - [Dancing☆Onigiri Preview](https://danonicw.skr.jp/) ([Repository](https://github.com/cwtickle/danoniplus-preview)) @cwtickle
124
124
  - [Dancing☆Onigiri エディター(CW Edition 対応)](https://github.com/superkuppabros/danoni-editor) @superkuppabros
125
125
  - [ダンおに曲データjs化ツール](https://github.com/suzme/danoni-base64) @suzme
126
126
  - [ダンおに矢印色ツール](https://github.com/suzme/danoni-colorpicker) @suzme
@@ -134,6 +134,7 @@ If you would like to cooperate with the development, please see below. Even if y
134
134
 
135
135
  ### Kirizma / キリズマ
136
136
 
137
+ - [Kirizma (CW Edition)](https://github.com/cwtickle/kirizma-cw) @cwtickle
137
138
  - [キリズマ譜面データ変換機](https://github.com/suzme/kirizma-converter) @suzme
138
139
  - [キリズマ歌詞表示作成ツール](https://github.com/prlg25/kirizma_lyric) @prlg25
139
140
  - [キリズマ難易度表](https://github.com/suzme/kirizma) @suzme
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 : 2026/03/26
7
+ * Revised : 2026/03/29
8
8
  *
9
9
  * https://github.com/cwtickle/danoniplus
10
10
  */
11
- const g_version = `Ver 46.4.0`;
12
- const g_revisedDate = `2026/03/26`;
11
+ const g_version = `Ver 46.5.0`;
12
+ const g_revisedDate = `2026/03/29`;
13
13
 
14
14
  // カスタム用バージョン (danoni_custom.js 等で指定可)
15
15
  let g_localVersion = ``;
@@ -1176,6 +1176,43 @@ const loadMultipleFiles2 = async (_fileData, _loadType) => {
1176
1176
  }));
1177
1177
  };
1178
1178
 
1179
+ /**
1180
+ * ユーザー定義のカスタム関数配列を安全に実行する共通関数
1181
+ * @param {string} _hookName - 識別名
1182
+ * @param {Function[]} _funcArray - カスタム関数の配列
1183
+ * @param {...any} args - 関数に渡したい引数(可変長)
1184
+ */
1185
+ const safeExecuteCustomHooks = (_hookName, _funcArray, ...args) => {
1186
+ if (!Array.isArray(_funcArray)) return true;
1187
+ const errorCache = g_errorCache[_hookName];
1188
+
1189
+ for (const [index, func] of _funcArray.entries()) {
1190
+ if (typeof func !== C_TYP_FUNCTION) continue; // 関数以外が入っていた場合の自衛
1191
+ if (errorCache && errorCache[index]?.has(func)) continue; // エラー検知済みの場合は以後スキップ
1192
+
1193
+ try {
1194
+ // ...args で受け取った引数をそのまま横流しして実行
1195
+ func(...args);
1196
+ } catch (e) {
1197
+
1198
+ if (!errorCache) {
1199
+ // ループしない場合のエラー処理
1200
+ console.group(`${unEscapeEmoji(g_emojiObj.crossMark)} Custom Function Error: [${_hookName}] (Index: ${index}${func.name ? `, Func: ${func.name}` : ``})`);
1201
+ console.error(e);
1202
+ console.groupEnd();
1203
+
1204
+ } else if (errorCache && !errorCache[index]?.has(func)) {
1205
+ // ループがある場合のエラー処理(初回のみ)
1206
+ console.group(`${unEscapeEmoji(g_emojiObj.crossMark)} Custom Function Error: [${_hookName}] (Index: ${index}${func.name ? `, Func: ${func.name}` : ``})`);
1207
+ console.error(`${unEscapeEmoji(g_emojiObj.policeLight)} [${g_scoreObj.baseFrame} Frame] ${g_msgObj.customFunctionError}`, e);
1208
+ console.groupEnd();
1209
+ errorCache[index] = new Set();
1210
+ errorCache[index].add(func);
1211
+ }
1212
+ }
1213
+ }
1214
+ }
1215
+
1179
1216
  /**
1180
1217
  * 与えられたパスより、キーワードとディレクトリに分割
1181
1218
  * @param {string} _fileName
@@ -2781,7 +2818,7 @@ const initialControl = async () => {
2781
2818
  }
2782
2819
  }
2783
2820
  }
2784
- g_customJsObj.preTitle.forEach(func => func());
2821
+ safeExecuteCustomHooks(`g_customJsObj.preTitle`, g_customJsObj.preTitle);
2785
2822
  const queryMusicId = getQueryParamVal(`musicId`);
2786
2823
  g_settings.musicIdxNum = queryMusicId !== null ? Number(queryMusicId) :
2787
2824
  g_headerObj.musicGroups?.[g_headerObj.musicNos[g_stateObj.scoreId]] ??
@@ -5395,7 +5432,7 @@ const titleInit = (_initFlg = false) => {
5395
5432
  }
5396
5433
 
5397
5434
  // ユーザカスタムイベント(初期)
5398
- g_customJsObj.title.forEach(func => func());
5435
+ safeExecuteCustomHooks(`g_customJsObj.title`, g_customJsObj.title);
5399
5436
 
5400
5437
  // バージョン情報取得
5401
5438
  let customVersion = ``;
@@ -5522,7 +5559,7 @@ const titleInit = (_initFlg = false) => {
5522
5559
  const flowTitleTimeline = () => {
5523
5560
 
5524
5561
  // ユーザカスタムイベント(フレーム毎)
5525
- g_customJsObj.titleEnterFrame.forEach(func => func());
5562
+ safeExecuteCustomHooks(`g_customJsObj.titleEnterFrame`, g_customJsObj.titleEnterFrame);
5526
5563
 
5527
5564
  // 背景・マスクモーション、スキン変更
5528
5565
  drawTitleResultMotion(g_currentPage);
@@ -5543,7 +5580,7 @@ const titleInit = (_initFlg = false) => {
5543
5580
  document.oncontextmenu = () => true;
5544
5581
  divRoot.oncontextmenu = () => false;
5545
5582
 
5546
- g_skinJsObj.title.forEach(func => func());
5583
+ safeExecuteCustomHooks(`g_skinJsObj.title`, g_skinJsObj.title);
5547
5584
  };
5548
5585
 
5549
5586
  /**
@@ -6038,7 +6075,7 @@ const changeMSelect = (_num, _initFlg = false) => {
6038
6075
  }
6039
6076
 
6040
6077
  // 選曲変更時のカスタム関数実行
6041
- g_customJsObj.musicSelect.forEach(func => func(g_settings.musicIdxNum));
6078
+ safeExecuteCustomHooks(`g_customJsObj.musicSelect`, g_customJsObj.musicSelect, g_settings.musicIdxNum);
6042
6079
  };
6043
6080
 
6044
6081
  /**
@@ -6268,7 +6305,7 @@ const dataMgtInit = () => {
6268
6305
  document.getElementById(`btnView${selectedKey}`).click();
6269
6306
 
6270
6307
  // ユーザカスタムイベント(初期)
6271
- g_customJsObj.dataMgt.forEach(func => func());
6308
+ safeExecuteCustomHooks(`g_customJsObj.dataMgt`, g_customJsObj.dataMgt);
6272
6309
 
6273
6310
  multiAppend(divRoot,
6274
6311
  createCss2Button(`btnBack`, g_lblNameObj.b_back, () => true, {
@@ -6360,7 +6397,7 @@ const dataMgtInit = () => {
6360
6397
  document.oncontextmenu = () => true;
6361
6398
  divRoot.oncontextmenu = () => false;
6362
6399
 
6363
- g_skinJsObj.dataMgt.forEach(func => func());
6400
+ safeExecuteCustomHooks(`g_skinJsObj.dataMgt`, g_skinJsObj.dataMgt);
6364
6401
  };
6365
6402
 
6366
6403
 
@@ -6438,7 +6475,7 @@ const preconditionInit = () => {
6438
6475
  }, g_cssObj.button_Setting));
6439
6476
 
6440
6477
  // ユーザカスタムイベント(初期)
6441
- g_customJsObj.precondition.forEach(func => func());
6478
+ safeExecuteCustomHooks(`g_customJsObj.precondition`, g_customJsObj.precondition);
6442
6479
 
6443
6480
  multiAppend(divRoot,
6444
6481
 
@@ -6461,7 +6498,7 @@ const preconditionInit = () => {
6461
6498
  document.oncontextmenu = () => true;
6462
6499
  divRoot.oncontextmenu = () => true;
6463
6500
 
6464
- g_skinJsObj.precondition.forEach(func => func());
6501
+ safeExecuteCustomHooks(`g_skinJsObj.precondition`, g_skinJsObj.precondition);
6465
6502
  };
6466
6503
 
6467
6504
  /*-----------------------------------------------------------*/
@@ -6580,7 +6617,7 @@ const updateSettingSummary = () => {
6580
6617
  `(Adj: ${g_stateObj.adjustment} f, Volume: ${g_stateObj.volume}%, ` +
6581
6618
  `ColorType: ${g_colorType}, KeyPattern: ${g_keyObj.currentPtn === -1 ? 'Self' : g_keyObj.currentPtn + 1})`;
6582
6619
 
6583
- g_customJsObj.settingSummary.forEach(func => func());
6620
+ safeExecuteCustomHooks(`g_customJsObj.settingSummary`, g_customJsObj.settingSummary);
6584
6621
  };
6585
6622
 
6586
6623
  /**
@@ -6615,7 +6652,7 @@ const optionInit = () => {
6615
6652
  createOptionWindow(divRoot);
6616
6653
 
6617
6654
  // ユーザカスタムイベント(初期)
6618
- g_customJsObj.option.forEach(func => func());
6655
+ safeExecuteCustomHooks(`g_customJsObj.option`, g_customJsObj.option);
6619
6656
 
6620
6657
  // ボタン描画
6621
6658
  commonSettingBtn(`Display`);
@@ -6625,7 +6662,7 @@ const optionInit = () => {
6625
6662
  document.oncontextmenu = () => true;
6626
6663
  g_initialFlg = true;
6627
6664
 
6628
- g_skinJsObj.option.forEach(func => func());
6665
+ safeExecuteCustomHooks(`g_skinJsObj.option`, g_skinJsObj.option);
6629
6666
  };
6630
6667
 
6631
6668
  /**
@@ -7600,7 +7637,7 @@ const setDifficulty = (_initFlg) => {
7600
7637
  lblMusicInfo.style.fontSize = wUnit(getFontSize2(lblMusicInfo.textContent, g_btnWidth(3 / 4), { maxSiz: 12 }));
7601
7638
 
7602
7639
  // ユーザカスタムイベント(初期)
7603
- g_customJsObj.difficulty.forEach(func => func(_initFlg, g_canLoadDifInfoFlg));
7640
+ safeExecuteCustomHooks(`g_customJsObj.difficulty`, g_customJsObj.difficulty, _initFlg, g_canLoadDifInfoFlg);
7604
7641
 
7605
7642
  // 設定サマリー表示の更新
7606
7643
  updateSettingSummary();
@@ -8498,7 +8535,7 @@ const settingsDisplayInit = () => {
8498
8535
  divRoot.appendChild(createDescDiv(`scMsg`, g_lblNameObj.sdShortcutDesc));
8499
8536
 
8500
8537
  // ユーザカスタムイベント(初期)
8501
- g_customJsObj.settingsDisplay.forEach(func => func());
8538
+ safeExecuteCustomHooks(`g_customJsObj.settingsDisplay`, g_customJsObj.settingsDisplay);
8502
8539
 
8503
8540
  // ボタン描画
8504
8541
  commonSettingBtn(`Settings`);
@@ -8507,7 +8544,7 @@ const settingsDisplayInit = () => {
8507
8544
  setShortcutEvent(g_currentPage);
8508
8545
  document.oncontextmenu = () => true;
8509
8546
 
8510
- g_skinJsObj.settingsDisplay.forEach(func => func());
8547
+ safeExecuteCustomHooks(`g_skinJsObj.settingsDisplay`, g_skinJsObj.settingsDisplay);
8511
8548
  };
8512
8549
 
8513
8550
  /**
@@ -8762,7 +8799,7 @@ const exSettingInit = () => {
8762
8799
  );
8763
8800
 
8764
8801
  // ユーザカスタムイベント(初期)
8765
- g_customJsObj.exSetting.forEach(func => func());
8802
+ safeExecuteCustomHooks(`g_customJsObj.exSetting`, g_customJsObj.exSetting);
8766
8803
 
8767
8804
  // 設定系のボタン群をまとめて作成(Data Save, Display切替, Back, KeyConfig, Playボタン)
8768
8805
  commonSettingBtn(g_currentPage);
@@ -8771,7 +8808,7 @@ const exSettingInit = () => {
8771
8808
  setShortcutEvent(g_currentPage, () => true, { dfEvtFlg: true });
8772
8809
  document.oncontextmenu = () => true;
8773
8810
 
8774
- g_skinJsObj.exSetting.forEach(func => func());
8811
+ safeExecuteCustomHooks(`g_skinJsObj.exSetting`, g_skinJsObj.exSetting);
8775
8812
  };
8776
8813
 
8777
8814
  /**
@@ -9594,7 +9631,7 @@ const keyConfigInit = (_kcType = g_kcType) => {
9594
9631
  };
9595
9632
 
9596
9633
  // ユーザカスタムイベント(初期)
9597
- g_customJsObj.keyconfig.forEach(func => func());
9634
+ safeExecuteCustomHooks(`g_customJsObj.keyconfig`, g_customJsObj.keyconfig);
9598
9635
 
9599
9636
  // 部分キー表示用ボタン描画
9600
9637
  if (configKeyGroupList.length > 1) {
@@ -9732,7 +9769,7 @@ const keyConfigInit = (_kcType = g_kcType) => {
9732
9769
  }
9733
9770
  });
9734
9771
 
9735
- g_skinJsObj.keyconfig.forEach(func => func());
9772
+ safeExecuteCustomHooks(`g_skinJsObj.keyconfig`, g_skinJsObj.keyconfig);
9736
9773
  document.onkeyup = evt => commonKeyUp(evt);
9737
9774
  document.oncontextmenu = () => false;
9738
9775
  };
@@ -9922,7 +9959,7 @@ const loadMusic = () => {
9922
9959
  lblLoading.textContent = `${g_lblNameObj.nowLoading} ${_event.loaded}Bytes`;
9923
9960
  }
9924
9961
  // ユーザカスタムイベント
9925
- g_customJsObj.progress.forEach(func => func(_event));
9962
+ safeExecuteCustomHooks(`g_customJsObj.progress`, g_customJsObj.progress, _event);
9926
9963
  });
9927
9964
 
9928
9965
  // エラー処理
@@ -10030,8 +10067,8 @@ const loadingScoreInit = async () => {
10030
10067
  g_headerObj.blankFrame = g_headerObj.blankFrameDef;
10031
10068
 
10032
10069
  // ユーザカスタムイベント
10033
- g_customJsObj.preloading.forEach(func => func());
10034
- g_skinJsObj.preloading.forEach(func => func());
10070
+ safeExecuteCustomHooks(`g_customJsObj.preloading`, g_customJsObj.preloading);
10071
+ safeExecuteCustomHooks(`g_skinJsObj.preloading`, g_skinJsObj.preloading);
10035
10072
 
10036
10073
  let dummyIdHeader = ``;
10037
10074
  if (g_stateObj.dummyId !== ``) {
@@ -10170,7 +10207,7 @@ const loadingScoreInit = async () => {
10170
10207
  getArrowSettings();
10171
10208
 
10172
10209
  // ユーザカスタムイベント
10173
- g_customJsObj.loading.forEach(func => func());
10210
+ safeExecuteCustomHooks(`g_customJsObj.loading`, g_customJsObj.loading);
10174
10211
 
10175
10212
  mainInit();
10176
10213
  };
@@ -12669,7 +12706,7 @@ const mainInit = () => {
12669
12706
  }
12670
12707
 
12671
12708
  // ユーザカスタムイベント(初期)
12672
- g_customJsObj.main.forEach(func => func());
12709
+ safeExecuteCustomHooks(`g_customJsObj.main`, g_customJsObj.main);
12673
12710
 
12674
12711
  /**
12675
12712
  * キーを押したときの処理
@@ -12917,7 +12954,7 @@ const mainInit = () => {
12917
12954
  if (_cnt === 0) {
12918
12955
  const stepDivHit = document.getElementById(`stepHit${_j}`);
12919
12956
 
12920
- g_customJsObj.dummyArrow.forEach(func => func());
12957
+ safeExecuteCustomHooks(`g_customJsObj.dummyArrow`, g_customJsObj.dummyArrow);
12921
12958
  stepDivHit.style.top = wUnit(-15);
12922
12959
  stepDivHit.style.opacity = 1;
12923
12960
  stepDivHit.classList.value = ``;
@@ -12937,7 +12974,7 @@ const mainInit = () => {
12937
12974
 
12938
12975
  // ダミーフリーズアロー(成功時)
12939
12976
  dummyFrzOK: (_j, _k, _frzName, _cnt) => {
12940
- g_customJsObj.dummyFrz.forEach(func => func());
12977
+ safeExecuteCustomHooks(`g_customJsObj.dummyFrz`, g_customJsObj.dummyFrz);
12941
12978
  $id(`frzHit${_j}`).opacity = 0;
12942
12979
  g_attrObj[_frzName].judgEndFlg = true;
12943
12980
  judgeObjDelete.dummyFrz(_j, _frzName);
@@ -13157,7 +13194,7 @@ const mainInit = () => {
13157
13194
  arrowSubRoot.appendChild(createColorObject2(`${_name}Top${_j}_${_arrowCnt}`, {
13158
13195
  background: _color, rotate: g_workObj.arrowRtn[_j],
13159
13196
  }));
13160
- g_customJsObj.makeArrow.forEach(func => func(_attrs, arrowName, _name, _arrowCnt));
13197
+ safeExecuteCustomHooks(`g_customJsObj.makeArrow`, g_customJsObj.makeArrow, _attrs, arrowName, _name, _arrowCnt);
13161
13198
  };
13162
13199
 
13163
13200
  /**
@@ -13326,7 +13363,7 @@ const mainInit = () => {
13326
13363
  addTransform(frzName, `rootX`, `translateX(${g_workObj.stepX[_j]}px)`);
13327
13364
  }
13328
13365
 
13329
- g_customJsObj.makeFrzArrow.forEach(func => func(_attrs, frzName, _name, _arrowCnt));
13366
+ safeExecuteCustomHooks(`g_customJsObj.makeFrzArrow`, g_customJsObj.makeFrzArrow, _attrs, frzName, _name, _arrowCnt);
13330
13367
  };
13331
13368
 
13332
13369
  /**
@@ -13445,7 +13482,7 @@ const mainInit = () => {
13445
13482
  }
13446
13483
 
13447
13484
  // ユーザカスタムイベント(フレーム毎)
13448
- g_customJsObj.mainEnterFrame.forEach(func => func());
13485
+ safeExecuteCustomHooks(`g_customJsObj.mainEnterFrame`, g_customJsObj.mainEnterFrame);
13449
13486
 
13450
13487
  // 速度変化 (途中変速, 個別加速)
13451
13488
  while (currentFrame >= g_workObj.speedData?.[speedCnts]) {
@@ -13643,7 +13680,7 @@ const mainInit = () => {
13643
13680
  g_timeoutEvtId = setTimeout(flowTimeline, 1000 / g_fps - buffTime);
13644
13681
  }
13645
13682
  };
13646
- g_skinJsObj.main.forEach(func => func());
13683
+ safeExecuteCustomHooks(`g_skinJsObj.main`, g_skinJsObj.main);
13647
13684
 
13648
13685
  g_audio.currentTime = firstFrame / g_fps * g_headerObj.playbackRate;
13649
13686
  g_audio.playbackRate = g_headerObj.playbackRate;
@@ -13863,7 +13900,7 @@ const changeAppearanceFilter = (_num = 10) => {
13863
13900
  }
13864
13901
 
13865
13902
  // ユーザカスタムイベント(アルファマスクの再描画)
13866
- g_customJsObj.appearanceFilter.forEach(func => func(topNum, bottomNum));
13903
+ safeExecuteCustomHooks(`g_customJsObj.appearanceFilter`, g_customJsObj.appearanceFilter, topNum, bottomNum);
13867
13904
 
13868
13905
  return doubleFilterFlg;
13869
13906
  };
@@ -14187,7 +14224,7 @@ const changeHitFrz = (_j, _k, _name, _difFrame = 0) => {
14187
14224
  if (g_stateObj.frzReturn !== C_FLG_OFF) {
14188
14225
  startFrzReturn();
14189
14226
  }
14190
- g_customJsObj[`judg_${_name}Hit`].forEach(func => func(_difFrame));
14227
+ safeExecuteCustomHooks(`g_customJsObj.judg_${_name}Hit`, g_customJsObj[`judg_${_name}Hit`], _difFrame);
14191
14228
  };
14192
14229
 
14193
14230
  /**
@@ -14444,7 +14481,7 @@ const judgeRecovery = (_name, _difFrame) => {
14444
14481
  if (_name === `shakin`) {
14445
14482
  quickRetry(`Shakin(Great)`);
14446
14483
  }
14447
- g_customJsObj[`judg_${_name}`].forEach(func => func(_difFrame));
14484
+ safeExecuteCustomHooks(`g_customJsObj.judg_${_name}`, g_customJsObj[`judg_${_name}`], _difFrame);
14448
14485
  };
14449
14486
 
14450
14487
  /**
@@ -14458,7 +14495,7 @@ const judgeDamage = (_name, _difFrame) => {
14458
14495
  comboJ.textContent = ``;
14459
14496
  diffJ.textContent = ``;
14460
14497
  lifeDamage();
14461
- g_customJsObj[`judg_${_name}`].forEach(func => func(_difFrame));
14498
+ safeExecuteCustomHooks(`g_customJsObj.judg_${_name}`, g_customJsObj[`judg_${_name}`], _difFrame);
14462
14499
  };
14463
14500
 
14464
14501
  /**
@@ -14483,7 +14520,7 @@ const judgeMatari = _difFrame => {
14483
14520
  finishViewing();
14484
14521
  quickRetry(`Matari(Good)`);
14485
14522
 
14486
- g_customJsObj.judg_matari.forEach(func => func(_difFrame));
14523
+ safeExecuteCustomHooks(`g_customJsObj.judg_matari`, g_customJsObj.judg_matari, _difFrame);
14487
14524
  };
14488
14525
 
14489
14526
  /**
@@ -14514,7 +14551,7 @@ const judgeKita = _difFrame => {
14514
14551
  lifeRecovery();
14515
14552
  finishViewing();
14516
14553
 
14517
- g_customJsObj.judg_kita.forEach(func => func(_difFrame));
14554
+ safeExecuteCustomHooks(`g_customJsObj.judg_kita`, g_customJsObj.judg_kita, _difFrame);
14518
14555
  };
14519
14556
 
14520
14557
  /**
@@ -14528,7 +14565,7 @@ const judgeIknai = _difFrame => {
14528
14565
 
14529
14566
  lifeDamage();
14530
14567
 
14531
- g_customJsObj.judg_iknai.forEach(func => func(_difFrame));
14568
+ safeExecuteCustomHooks(`g_customJsObj.judg_iknai`, g_customJsObj.judg_iknai, _difFrame);
14532
14569
  };
14533
14570
 
14534
14571
  const jdgList = [`ii`, `shakin`, `matari`, `shobon`].map(jdg => toCapitalize(jdg));
@@ -14990,7 +15027,7 @@ const resultInit = () => {
14990
15027
 
14991
15028
  // ユーザカスタムイベント(初期)
14992
15029
  const currentDateTime = new Date().toLocaleString();
14993
- g_customJsObj.result.forEach(func => func());
15030
+ safeExecuteCustomHooks(`g_customJsObj.result`, g_customJsObj.result);
14994
15031
 
14995
15032
  if (highscorePreCondition) {
14996
15033
 
@@ -15334,7 +15371,7 @@ const resultInit = () => {
15334
15371
  const flowResultTimeline = () => {
15335
15372
 
15336
15373
  // ユーザカスタムイベント(フレーム毎)
15337
- g_customJsObj.resultEnterFrame.forEach(func => func());
15374
+ safeExecuteCustomHooks(`g_customJsObj.resultEnterFrame`, g_customJsObj.resultEnterFrame);
15338
15375
 
15339
15376
  // 背景・マスクモーション、スキン変更
15340
15377
  drawTitleResultMotion(g_currentPage);
@@ -15368,7 +15405,7 @@ const resultInit = () => {
15368
15405
  setShortcutEvent(g_currentPage, () => true, { dfEvtFlg: true });
15369
15406
  document.oncontextmenu = () => true;
15370
15407
 
15371
- g_skinJsObj.result.forEach(func => func());
15408
+ safeExecuteCustomHooks(`g_skinJsObj.result`, g_skinJsObj.result);
15372
15409
  };
15373
15410
 
15374
15411
  /**
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2019/11/19
8
- * Revised : 2026/03/26 (v46.4.0)
8
+ * Revised : 2026/03/29 (v46.5.0)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -963,6 +963,7 @@ const g_escapeStr = {
963
963
  const g_emojiObj = {
964
964
  checkMark: `✔`, // チェックマーク (check mark)
965
965
  crossMark: `❌`, // バツ (cross mark)
966
+ policeLight: `🚨`, // パトランプ (police car light)
966
967
  muted: `🔇`, // 無音のスピーカー (muted speaker)
967
968
  speaker: `🔊`, // 音量大のスピーカー (speaker high volume)
968
969
  shield: `🛡`, // 盾 (shield)
@@ -4827,6 +4828,8 @@ const g_lang_msgObj = {
4827
4828
  pickColorR: `設定する矢印色の種類を切り替えます。`,
4828
4829
  pickColorCopy: `このボタンを押すと、フリーズアローの配色を矢印(枠)の色で上書きします。\nヒット時のフリーズアローの色も上書きします。`,
4829
4830
  pickColorReset: `矢印・フリーズアローの配色を元に戻します。`,
4831
+
4832
+ customFunctionError: `初回の実行エラーが発生しました。修正されるまでこの処理は無視されます。`,
4830
4833
  },
4831
4834
 
4832
4835
  En: {
@@ -4925,6 +4928,8 @@ const g_lang_msgObj = {
4925
4928
  pickColorR: `Switches the arrow color type to be set.`,
4926
4929
  pickColorCopy: `Pressing this button will override the color scheme of the freeze arrow with the frame color of the arrow. \nIt also overrides the color of the freeze arrow on hit.`,
4927
4930
  pickColorReset: `Restore the color scheme for arrows and freeze arrows.`,
4931
+
4932
+ customFunctionError: `An error occurred during the first execution. This process will be ignored until the error is resolved.`,
4928
4933
  },
4929
4934
 
4930
4935
  };
@@ -5009,6 +5014,12 @@ const g_skinJsObj = {
5009
5014
  result: [],
5010
5015
  };
5011
5016
 
5017
+ const g_errorCache = {
5018
+ 'g_customJsObj.titleEnterFrame': [],
5019
+ 'g_customJsObj.mainEnterFrame': [],
5020
+ 'g_customJsObj.resultEnterFrame': [],
5021
+ };
5022
+
5012
5023
  /**
5013
5024
  * 従来のカスタム関数をg_customJsObj, g_skinJsObjへ追加
5014
5025
  * - customjsファイルを読み込んだ直後にこの関数を呼び出している
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "danoniplus",
3
- "version": "46.4.0",
3
+ "version": "46.5.0",
4
4
  "description": "Dancing☆Onigiri (CW Edition) - Web-based Rhythm Game",
5
5
  "main": "./js/danoni_main.js",
6
6
  "jsdelivr": "./js/danoni_main.js",