danoniplus 50.1.2 → 50.3.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 : 2026/08/31
7
+ * Revised : 2026/09/08
8
8
  *
9
9
  * https://github.com/cwtickle/danoniplus
10
10
  */
11
- const g_version = `Ver 50.1.2`;
12
- const g_revisedDate = `2026/08/31`;
11
+ const g_version = `Ver 50.3.0`;
12
+ const g_revisedDate = `2026/09/08`;
13
13
 
14
14
  // カスタム用バージョン (danoni_custom.js 等で指定可)
15
15
  let g_localVersion = ``;
@@ -1757,7 +1757,7 @@ const createColorPicker = (_parentObj, _id, _func, { x = 0, y = 0 } = {}) => {
1757
1757
  picker.style.top = wUnit(y);
1758
1758
  picker.style.position = `absolute`;
1759
1759
  picker.style.pointerEvents = C_DIS_AUTO;
1760
- picker.addEventListener(`change`, _func);
1760
+ g_handler.addListener(picker, `change`, _func);
1761
1761
  _parentObj.appendChild(picker);
1762
1762
  return picker;
1763
1763
  };
@@ -1871,16 +1871,16 @@ const g_handler = (() => {
1871
1871
  * @param {EventTarget} _target
1872
1872
  * @param {string} _type
1873
1873
  * @param {EventListenerOrEventListenerObject} _listener
1874
- * @param {boolean} [_capture=false]
1874
+ * @param {boolean|AddEventListenerOptions} [_options=false]
1875
1875
  * @returns {number}
1876
1876
  */
1877
- addListener: (_target, _type, _listener, _capture = false) => {
1878
- _target.addEventListener(_type, _listener, _capture);
1877
+ addListener: (_target, _type, _listener, _options = false) => {
1878
+ _target.addEventListener(_type, _listener, _options);
1879
1879
  events[key] = {
1880
1880
  target: _target,
1881
1881
  type: _type,
1882
1882
  listener: _listener,
1883
- capture: _capture
1883
+ options: _options // boolean/objectどちらでもそのまま保持
1884
1884
  };
1885
1885
  return key++;
1886
1886
  },
@@ -1891,9 +1891,104 @@ const g_handler = (() => {
1891
1891
  removeListener: key => {
1892
1892
  if (key in events) {
1893
1893
  const e = events[key];
1894
- e.target.removeEventListener(e.type, e.listener, e.capture);
1894
+ e.target.removeEventListener(e.type, e.listener, e.options);
1895
1895
  delete events[key];
1896
1896
  }
1897
+ },
1898
+ /**
1899
+ * すべてのイベントリスナーを削除
1900
+ */
1901
+ removeAll: () => {
1902
+ Object.values(events).forEach(e => {
1903
+ e.target.removeEventListener(e.type, e.listener, e.options);
1904
+ });
1905
+ for (const k in events) delete events[k];
1906
+ },
1907
+ /**
1908
+ * 指定した要素配下(自身含む)のイベントリスナーをまとめて削除
1909
+ * @param {HTMLDivElement} _container
1910
+ */
1911
+ removeByContainer: _container => {
1912
+ Object.entries(events).forEach(([k, e]) => {
1913
+ if (_container.contains(e.target)) {
1914
+ e.target.removeEventListener(e.type, e.listener, e.options);
1915
+ delete events[k];
1916
+ }
1917
+ });
1918
+ }
1919
+ };
1920
+ })();
1921
+
1922
+ // setInterval/setTimeout版
1923
+ const g_timerHandler = (() => {
1924
+ const timers = {}; // key -> { id, type: 'interval' | 'timeout' }
1925
+ let key = 0;
1926
+
1927
+ const clearByType = entry => {
1928
+ if (entry.type === 'interval') {
1929
+ clearInterval(entry.id);
1930
+ } else {
1931
+ clearTimeout(entry.id);
1932
+ }
1933
+ };
1934
+
1935
+ const clear = key => {
1936
+ if (key in timers) {
1937
+ clearByType(timers[key]);
1938
+ delete timers[key];
1939
+ }
1940
+ };
1941
+
1942
+ return {
1943
+ setInterval: (_callback, _ms) => {
1944
+ const id = setInterval(_callback, _ms);
1945
+ timers[key] = { id, type: 'interval' };
1946
+ return key++;
1947
+ },
1948
+ setTimeout: (_callback, _ms, ..._args) => {
1949
+ const id = setTimeout(() => {
1950
+ _callback(..._args);
1951
+ delete timers[myKey]; // 発火し終わったら自動で登録簿から消す
1952
+ }, _ms);
1953
+ const myKey = key;
1954
+ timers[key] = { id, type: 'timeout' };
1955
+ return key++;
1956
+ },
1957
+ clear,
1958
+ clearInterval: clear,
1959
+ clearTimeout: clear,
1960
+ clearAll: () => {
1961
+ Object.values(timers).forEach(clearByType);
1962
+ for (const k in timers) delete timers[k];
1963
+ }
1964
+ };
1965
+ })();
1966
+
1967
+ // requestAnimationFrame版
1968
+ const g_rafHandler = (() => {
1969
+ const loops = {};
1970
+ let key = 0;
1971
+
1972
+ return {
1973
+ start: _callback => {
1974
+ const myKey = key++;
1975
+ function loop() {
1976
+ if (!(myKey in loops)) return; // clearAll済みなら自然停止
1977
+ _callback();
1978
+ loops[myKey] = requestAnimationFrame(loop);
1979
+ }
1980
+ loops[myKey] = requestAnimationFrame(loop);
1981
+ return myKey;
1982
+ },
1983
+ stop: key => {
1984
+ if (key in loops) {
1985
+ cancelAnimationFrame(loops[key]);
1986
+ delete loops[key];
1987
+ }
1988
+ },
1989
+ stopAll: () => {
1990
+ Object.values(loops).forEach(id => cancelAnimationFrame(id));
1991
+ for (const k in loops) delete loops[k];
1897
1992
  }
1898
1993
  };
1899
1994
  })();
@@ -1905,10 +2000,8 @@ const g_handler = (() => {
1905
2000
  const deleteChildspriteAll = _parentObjName => {
1906
2001
 
1907
2002
  const parentsprite = document.getElementById(_parentObjName);
2003
+ g_handler.removeByContainer(parentsprite);
1908
2004
  while (parentsprite.hasChildNodes()) {
1909
- g_handler.removeListener(parentsprite.firstChild.getAttribute(`lsnrkey`));
1910
- g_handler.removeListener(parentsprite.firstChild.getAttribute(`lsnrkeyTS`));
1911
- g_handler.removeListener(parentsprite.firstChild.getAttribute(`lsnrkeyTE`));
1912
2005
  parentsprite.removeChild(parentsprite.firstChild);
1913
2006
  }
1914
2007
  };
@@ -1920,6 +2013,7 @@ const deleteChildspriteAll = _parentObjName => {
1920
2013
  */
1921
2014
  const deleteDiv = (_parentId, _idName) => {
1922
2015
  if (document.getElementById(_idName) !== null) {
2016
+ g_handler.removeByContainer(document.getElementById(_idName));
1923
2017
  _parentId.removeChild(document.getElementById(_idName));
1924
2018
  }
1925
2019
  };
@@ -1969,13 +2063,13 @@ const createCss2Button = (_id, _text, _func = () => true, {
1969
2063
  (g_initialFlg && g_btnWaitFrame[groupName].initial)) {
1970
2064
  } else {
1971
2065
  style.pointerEvents = C_DIS_NONE;
1972
- setTimeout(() => style.pointerEvents = rest.pointerEvents ?? C_DIS_AUTO,
2066
+ g_timerHandler.setTimeout(() => style.pointerEvents = rest.pointerEvents ?? C_DIS_AUTO,
1973
2067
  g_btnWaitFrame[groupName].b_frame * 1000 / g_fps);
1974
2068
  }
1975
2069
  }
1976
2070
 
1977
2071
  // ボタンを押したときの動作
1978
- const lsnrkey = g_handler.addListener(div, `click`, evt => {
2072
+ g_handler.addListener(div, `click`, evt => {
1979
2073
  if (!setBoolVal(g_btnDeleteFlg[_id])) {
1980
2074
  _func(evt);
1981
2075
  }
@@ -2006,9 +2100,6 @@ const createCss2Button = (_id, _text, _func = () => true, {
2006
2100
  return false;
2007
2101
  };
2008
2102
 
2009
- // イベントリスナー用のキーをセット
2010
- div.setAttribute(`lsnrkey`, lsnrkey);
2011
-
2012
2103
  return div;
2013
2104
  };
2014
2105
 
@@ -2788,7 +2879,7 @@ const warmUpAudioContext = async () => {
2788
2879
  const baseTime = ctx.currentTime;
2789
2880
  const limitTime = performance.now() + 500;
2790
2881
  while (ctx.currentTime === baseTime && performance.now() < limitTime) {
2791
- await new Promise(resolve => setTimeout(resolve, 10));
2882
+ await new Promise(resolve => g_timerHandler.setTimeout(resolve, 10));
2792
2883
  }
2793
2884
  };
2794
2885
 
@@ -5905,7 +5996,7 @@ const titleInit = (_initFlg = false) => {
5905
5996
  createDivCss2Label(`lblMusicSelectDetail`, ``, g_lblPosObj.lblMusicSelectDetail),
5906
5997
  createCss2Button(`btnStart`,
5907
5998
  `>`, () => {
5908
- clearTimeout(g_timeoutEvtTitleId);
5999
+ g_timerHandler.clearTimeout(g_timeoutEvtTitleId);
5909
6000
  g_handler.removeListener(wheelHandler);
5910
6001
  g_keyObj.prevKey = `Dummy${g_settings.musicIdxNum}`;
5911
6002
  g_langStorage.bgmVolume = g_stateObj.bgmVolume;
@@ -5992,14 +6083,14 @@ const titleInit = (_initFlg = false) => {
5992
6083
 
5993
6084
  let spriteOpacity = 1;
5994
6085
  let fadeOpacity = null;
5995
- const fadeStartOpacity = setTimeout(() => {
5996
- clearTimeout(fadeStartOpacity);
6086
+ const fadeStartOpacity = g_timerHandler.setTimeout(() => {
6087
+ g_timerHandler.clearTimeout(fadeStartOpacity);
5997
6088
  setOpacity(spriteOpacity);
5998
6089
  }, 2000);
5999
6090
 
6000
6091
  const setOpacity = (_opacity) => {
6001
6092
  if (_opacity <= 0) {
6002
- clearTimeout(fadeOpacity);
6093
+ g_timerHandler.clearTimeout(fadeOpacity);
6003
6094
  mSelectTitleSprite.style.display = C_DIS_NONE;
6004
6095
  if (!g_stateObj.bgmMuteFlg && g_audioForMS) {
6005
6096
  g_audioForMS.muted = false;
@@ -6012,7 +6103,7 @@ const titleInit = (_initFlg = false) => {
6012
6103
  }
6013
6104
  } else {
6014
6105
  mSelectTitleSprite.style.opacity = _opacity;
6015
- fadeOpacity = setTimeout(() => {
6106
+ fadeOpacity = g_timerHandler.setTimeout(() => {
6016
6107
  spriteOpacity -= 0.25;
6017
6108
  setOpacity(spriteOpacity);
6018
6109
  }, 50);
@@ -6074,7 +6165,7 @@ const titleInit = (_initFlg = false) => {
6074
6165
 
6075
6166
  // Click Here
6076
6167
  createCss2Button(`btnStart`, g_lblNameObj.clickHere, () => {
6077
- clearTimeout(g_timeoutEvtTitleId);
6168
+ g_timerHandler.clearTimeout(g_timeoutEvtTitleId);
6078
6169
  g_keyObj.prevKey = `Dummy${g_settings.musicIdxNum}`;
6079
6170
  }, {
6080
6171
  x: g_btnX(), w: g_btnWidth(), siz: g_limitObj.titleSiz, resetFunc: () => optionInit(),
@@ -6093,7 +6184,7 @@ const titleInit = (_initFlg = false) => {
6093
6184
 
6094
6185
  // Reset
6095
6186
  createCss2Button(`btnReset`, g_lblNameObj.dataReset, () => {
6096
- clearTimeout(g_timeoutEvtTitleId);
6187
+ g_timerHandler.clearTimeout(g_timeoutEvtTitleId);
6097
6188
  g_handler.removeListener(wheelHandler);
6098
6189
  dataMgtInit();
6099
6190
  }, g_lblPosObj.btnReset, g_cssObj.button_Reset),
@@ -6173,10 +6264,10 @@ const titleInit = (_initFlg = false) => {
6173
6264
 
6174
6265
  g_scoreObj.titleFrameNum++;
6175
6266
  g_animationData.forEach(sprite => g_scoreObj[`${sprite}TitleFrameNum`]++);
6176
- g_timeoutEvtTitleId = setTimeout(flowTitleTimeline, 1000 / g_fps - buffTime);
6267
+ g_timeoutEvtTitleId = g_timerHandler.setTimeout(flowTitleTimeline, 1000 / g_fps - buffTime);
6177
6268
  };
6178
6269
 
6179
- g_timeoutEvtTitleId = setTimeout(flowTitleTimeline, 1000 / g_fps);
6270
+ g_timeoutEvtTitleId = g_timerHandler.setTimeout(flowTitleTimeline, 1000 / g_fps);
6180
6271
 
6181
6272
  // キー操作イベント(デフォルト)
6182
6273
  setShortcutEvent(g_currentPage, () => true, { dfEvtFlg: true });
@@ -6315,7 +6406,7 @@ const pauseBGM = () => {
6315
6406
  }
6316
6407
  [`bgmLooped`, `bgmFadeIn`, `bgmFadeOut`].forEach(id => {
6317
6408
  if (g_stateObj[id]) {
6318
- clearTimeout(g_stateObj[id]);
6409
+ g_timerHandler.clearTimeout(g_stateObj[id]);
6319
6410
  g_stateObj[id] = null;
6320
6411
  }
6321
6412
  });
@@ -6383,10 +6474,10 @@ const playBGM = async (_num, _currentLoopNum = g_settings.musicLoopNum) => {
6383
6474
  g_audioForMS.volume = Math.min(Math.max(volume, 0), 1);
6384
6475
 
6385
6476
  // 次のステップへ
6386
- setTimeout(stepFunc, FADE_INTERVAL_MS);
6477
+ g_timerHandler.setTimeout(stepFunc, FADE_INTERVAL_MS);
6387
6478
  };
6388
6479
 
6389
- return setTimeout(stepFunc, FADE_INTERVAL_MS);
6480
+ return g_timerHandler.setTimeout(stepFunc, FADE_INTERVAL_MS);
6390
6481
  };
6391
6482
 
6392
6483
  /**
@@ -6411,10 +6502,10 @@ const playBGM = async (_num, _currentLoopNum = g_settings.musicLoopNum) => {
6411
6502
  }
6412
6503
 
6413
6504
  // 次のチェックへ
6414
- setTimeout(step, FADE_INTERVAL_MS);
6505
+ g_timerHandler.setTimeout(step, FADE_INTERVAL_MS);
6415
6506
  };
6416
6507
 
6417
- return setTimeout(step, FADE_INTERVAL_MS);
6508
+ return g_timerHandler.setTimeout(step, FADE_INTERVAL_MS);
6418
6509
  };
6419
6510
 
6420
6511
  /**
@@ -6438,7 +6529,7 @@ const playBGM = async (_num, _currentLoopNum = g_settings.musicLoopNum) => {
6438
6529
  g_audioForMS.currentTime = musicStart;
6439
6530
 
6440
6531
  if (isTitle()) {
6441
- setTimeout(() => {
6532
+ g_timerHandler.setTimeout(() => {
6442
6533
  fadeIn();
6443
6534
  if (encodeFlg) repeatBGM();
6444
6535
  }, FADE_DELAY_MS);
@@ -6700,7 +6791,7 @@ const changeMSelect = (_num, _initFlg = false) => {
6700
6791
  if (_initFlg) {
6701
6792
  playBGM(_num);
6702
6793
  } else {
6703
- setTimeout(() => {
6794
+ g_timerHandler.setTimeout(() => {
6704
6795
  if (currentLoopNum === g_settings.musicLoopNum) {
6705
6796
  playBGM(_num, currentLoopNum);
6706
6797
  }
@@ -8528,25 +8619,26 @@ const resolveKeyFamily = () => {
8528
8619
  const allPaths = new Set(g_familyObj.families.flatMap(f => Object.keys(f.fields)));
8529
8620
  allPaths.forEach(path => {
8530
8621
  const spec = newFamily?.fields[path];
8531
- // 該当プロパティのカスタム処理があれば優先し、なければ通常のプロパティ代入を使用
8532
- const applier = g_familyObj.families.map(f => f.appliers[path]).find(Boolean) ?? (v => setPathVal(path, v));
8533
8622
 
8534
8623
  if (spec !== undefined) {
8535
8624
  // 【変更値の適用】現在のファミリーにそのプロパティの設定がある場合
8536
8625
  // 初めて書き換えるプロパティの場合のみ、元の値をスナップショット(初期値)として退避
8537
8626
  if (!g_familyObj.ownership[path]) {
8538
8627
  g_familyObj.snapshot[path] = getPathVal(path);
8539
- g_familyObj.ownership[path] = true;
8540
8628
  }
8541
8629
  // 関数なら評価し、値ならそのまま適用値とする
8630
+ g_familyObj.ownership[path] = newFamily;
8631
+ const applier = newFamily.appliers[path] ?? (v => setPathVal(path, v));
8542
8632
  applyIfChanged(path, typeof spec === `function` ? spec() : spec, applier);
8633
+
8543
8634
  } else if (g_familyObj.ownership[path]) {
8544
8635
  // 【標準への復元】別のファミリーに移行し、かつ元々自分が書き換えていたプロパティの場合
8545
8636
  // スナップショットから元の値(初期値)を復元し、オーナーシップを解放
8637
+ const applier = g_familyObj.ownership[path].appliers[path] ?? (v => setPathVal(path, v));
8546
8638
  applyIfChanged(path, g_familyObj.snapshot[path], applier);
8547
- g_familyObj.ownership[path] = false;
8639
+ g_familyObj.ownership[path] = null;
8548
8640
  }
8549
- // ownership[path]がfalseのままなら、標準同士の切り替えでも一切触らない
8641
+ // ownership[path]がnullのままなら、標準同士の切り替えでも一切触らない
8550
8642
  });
8551
8643
  };
8552
8644
 
@@ -8890,7 +8982,7 @@ const createOptionWindow = _sprite => {
8890
8982
  );
8891
8983
 
8892
8984
  const fadeinSlider = document.getElementById(`fadeinSlider`);
8893
- fadeinSlider.addEventListener(`input`, () => {
8985
+ g_handler.addListener(fadeinSlider, `input`, () => {
8894
8986
  g_stateObj.fadein = inputSlider(fadeinSlider, lnkFadein, `fadein`);
8895
8987
  updateSettingSummary();
8896
8988
  drawMinimap(g_stateObj.scoreId, { _fadeinFlg: true });
@@ -9515,9 +9607,6 @@ const settingsDisplayInit = () => {
9515
9607
  /** プレビューウィンドウのルートdiv */
9516
9608
  let g_previewRoot = null;
9517
9609
 
9518
- /** プレビューで登録した一時リスナー群 */
9519
- let g_previewLsnrKeys = new Set();
9520
-
9521
9610
  /** プレビュー内の各UIオブジェクトの現在座標 */
9522
9611
  const g_previewPos = {
9523
9612
  arrowJdg: { x: null, y: null }, // 通常判定キャラクタ・コンボ
@@ -9525,15 +9614,6 @@ const g_previewPos = {
9525
9614
  shortcut: { x: null, y: null },
9526
9615
  };
9527
9616
 
9528
- /**
9529
- * プレビュー用リスナー登録(キーをレジストリへ格納)
9530
- */
9531
- const addPreviewListener = (target, type, listener, capture = false) => {
9532
- const key = g_handler.addListener(target, type, listener, capture);
9533
- g_previewLsnrKeys.add(key);
9534
- return key;
9535
- };
9536
-
9537
9617
  /**
9538
9618
  * プレビューのトグル(表示 / 非表示)
9539
9619
  */
@@ -9621,11 +9701,6 @@ const closeDisplayPreview = () => {
9621
9701
  deleteChildspriteAll(`displayPreviewOverlay`);
9622
9702
  overlay.remove();
9623
9703
  }
9624
- // プレビュー専用に登録した残りのハンドラを明示解除
9625
- if (g_previewLsnrKeys?.size) {
9626
- g_previewLsnrKeys.forEach(k => g_handler.removeListener(k));
9627
- g_previewLsnrKeys.clear();
9628
- }
9629
9704
  g_previewRoot = null;
9630
9705
  };
9631
9706
 
@@ -9974,7 +10049,7 @@ const makeElementDraggable = (_target, _key, _playW, _playH, _bounds, _config) =
9974
10049
  background: `rgba(${bgColor},0.04)`,
9975
10050
  });
9976
10051
 
9977
- const keyDown = addPreviewListener(_target, `pointerdown`, _evt => {
10052
+ g_handler.addListener(_target, `pointerdown`, _evt => {
9978
10053
  dragging = true;
9979
10054
  dragStartX = _evt.clientX;
9980
10055
  dragStartY = _evt.clientY;
@@ -9985,7 +10060,7 @@ const makeElementDraggable = (_target, _key, _playW, _playH, _bounds, _config) =
9985
10060
  _evt.stopPropagation();
9986
10061
  });
9987
10062
 
9988
- const keyMove = addPreviewListener(_target, `pointermove`, _evt => {
10063
+ g_handler.addListener(_target, `pointermove`, _evt => {
9989
10064
  if (!dragging) return;
9990
10065
 
9991
10066
  // 1. マウスの実際の移動量を計算
@@ -10006,7 +10081,7 @@ const makeElementDraggable = (_target, _key, _playW, _playH, _bounds, _config) =
10006
10081
  _evt.stopPropagation();
10007
10082
  });
10008
10083
 
10009
- const keyUp = addPreviewListener(_target, `pointerup`, _evt => {
10084
+ g_handler.addListener(_target, `pointerup`, _evt => {
10010
10085
  if (!dragging) return;
10011
10086
  dragging = false;
10012
10087
  _target.style.cursor = `grab`;
@@ -10024,15 +10099,10 @@ const makeElementDraggable = (_target, _key, _playW, _playH, _bounds, _config) =
10024
10099
  _evt.stopPropagation();
10025
10100
  });
10026
10101
 
10027
- addPreviewListener(_target, `pointercancel`, _evt => {
10102
+ g_handler.addListener(_target, `pointercancel`, _evt => {
10028
10103
  dragging = false;
10029
10104
  _target.style.cursor = `grab`;
10030
10105
  });
10031
-
10032
- // 既存の管理用属性(必要に応じて)
10033
- _target.setAttribute(`lsnrkey`, keyMove);
10034
- _target.setAttribute(`lsnrkeyTS`, keyDown);
10035
- _target.setAttribute(`lsnrkeyTE`, keyUp);
10036
10106
  };
10037
10107
 
10038
10108
  /**
@@ -10086,8 +10156,8 @@ const showToast = _msg => {
10086
10156
  opacity: `1`,
10087
10157
  });
10088
10158
  divRoot.appendChild(toast);
10089
- setTimeout(() => { toast.style.opacity = `0`; }, 2200);
10090
- setTimeout(() => { if (toast.parentNode) toast.remove(); }, 2700);
10159
+ g_timerHandler.setTimeout(() => { toast.style.opacity = `0`; }, 2200);
10160
+ g_timerHandler.setTimeout(() => { if (toast.parentNode) toast.remove(); }, 2700);
10091
10161
  };
10092
10162
 
10093
10163
  /**
@@ -10211,7 +10281,7 @@ const createSettingsDisplayWindow = _sprite => {
10211
10281
  };
10212
10282
 
10213
10283
  const appearanceSlider = document.getElementById(`appearanceSlider`);
10214
- appearanceSlider.addEventListener(`input`, () => {
10284
+ g_handler.addListener(appearanceSlider, `input`, () => {
10215
10285
  g_hidSudObj.filterPos = inputSlider(appearanceSlider, lblAppearancePos, `appearance`);
10216
10286
  updateSettingSummary();
10217
10287
  }, false);
@@ -12500,8 +12570,8 @@ const fetchMusicBlobUrl = (_url, _lblLoading) => new Promise((resolve, reject) =
12500
12570
 
12501
12571
  // 停滞タイマーをリセット
12502
12572
  const resetStallTimer = () => {
12503
- clearTimeout(stallTimer);
12504
- stallTimer = setTimeout(() => {
12573
+ g_timerHandler.clearTimeout(stallTimer);
12574
+ stallTimer = g_timerHandler.setTimeout(() => {
12505
12575
  request.abort();
12506
12576
  makeWarningWindow(g_msgInfoObj.E_0033, { backBtnUse: true });
12507
12577
  reject(new Error(`stalled`));
@@ -12510,7 +12580,7 @@ const fetchMusicBlobUrl = (_url, _lblLoading) => new Promise((resolve, reject) =
12510
12580
 
12511
12581
  // 読み込み完了時
12512
12582
  request.addEventListener(`load`, () => {
12513
- clearTimeout(stallTimer);
12583
+ g_timerHandler.clearTimeout(stallTimer);
12514
12584
  if (request.status >= 200 && request.status < 300) {
12515
12585
  const blobUrl = URL.createObjectURL(request.response);
12516
12586
  createEmptySprite(divRoot, `loader`, g_windowObj.loader);
@@ -12540,7 +12610,7 @@ const fetchMusicBlobUrl = (_url, _lblLoading) => new Promise((resolve, reject) =
12540
12610
  });
12541
12611
 
12542
12612
  request.addEventListener(`error`, () => {
12543
- clearTimeout(stallTimer);
12613
+ g_timerHandler.clearTimeout(stallTimer);
12544
12614
  makeWarningWindow(g_msgInfoObj.E_0034, { backBtnUse: true });
12545
12615
  reject(new Error(`network error`));
12546
12616
  });
@@ -14815,7 +14885,7 @@ const getArrowSettings = () => {
14815
14885
  g_workObj.frzReturnFlg = false;
14816
14886
  g_workObj.frzReturnSeq = g_frzReturnSeqFunc.get(g_stateObj.frzReturnType)();
14817
14887
  if (g_workObj.frzReturnTimerId) {
14818
- clearTimeout(g_workObj.frzReturnTimerId);
14888
+ g_timerHandler.clearTimeout(g_workObj.frzReturnTimerId);
14819
14889
  g_workObj.frzReturnTimerId = null;
14820
14890
  }
14821
14891
 
@@ -15479,7 +15549,7 @@ const mainInit = () => {
15479
15549
  if (g_isMac && keyIsShift()) {
15480
15550
  // Mac OS、IPad OSはDeleteキーが無いためShift+BSで代用
15481
15551
  g_audio.pause();
15482
- clearTimeout(g_timeoutEvtId);
15552
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
15483
15553
  titleInit();
15484
15554
 
15485
15555
  } else {
@@ -15489,7 +15559,7 @@ const mainInit = () => {
15489
15559
 
15490
15560
  } else if (setCode === g_kCdN[g_headerObj.keyTitleBack]) {
15491
15561
  g_audio.pause();
15492
- clearTimeout(g_timeoutEvtId);
15562
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
15493
15563
  if (keyIsShift()) {
15494
15564
  if (g_currentArrows !== g_fullArrows || g_stateObj.lifeMode === C_LFE_BORDER && g_workObj.lifeVal < g_workObj.lifeBorder) {
15495
15565
  g_gameOverFlg = true;
@@ -15897,6 +15967,10 @@ const mainInit = () => {
15897
15967
  // 矢印色の設定
15898
15968
  // - 枠/塗りつぶし色: g_attrObj[arrowName].Arrow / ArrowShadow
15899
15969
  g_typeLists.arrowColor.forEach(val => g_attrObj[arrowName][`Arrow${val}`] = g_workObj[`${_name}${val}Colors`][_j]);
15970
+
15971
+ // g_attrObj定義後のカスタムイベント
15972
+ safeExecuteCustomHooks(`g_customJsObj.preMakeArrow`, g_customJsObj.preMakeArrow, _attrs, arrowName, _name, _arrowCnt);
15973
+
15900
15974
  arrowSprite[g_workObj.dividePos[_j]].appendChild(arrowRoot);
15901
15975
  const arrowSubRoot = createEmptySprite(arrowRoot, `sub${arrowName}`, { x: 0, y: 0, w: C_ARW_WIDTH, h: C_ARW_WIDTH });
15902
15976
 
@@ -16037,6 +16111,10 @@ const mainInit = () => {
16037
16111
  g_attrObj[frzName][`${val}All`] = g_workObj[`${_name}${val}ColorsAll`][_j];
16038
16112
  }
16039
16113
  });
16114
+
16115
+ // g_attrObj定義後のカスタムイベント
16116
+ safeExecuteCustomHooks(`g_customJsObj.preMakeFrzArrow`, g_customJsObj.preMakeFrzArrow, _attrs, frzName, _name, _arrowCnt);
16117
+
16040
16118
  arrowSprite[g_workObj.dividePos[_j]].appendChild(frzRoot);
16041
16119
  let shadowColor = _shadowColor === `Default` ? _normalColor : _shadowColor;
16042
16120
  const frzSubRoot = createEmptySprite(frzRoot, `sub${frzName}`, { x: 0, y: 0, w: C_ARW_WIDTH, h: C_ARW_WIDTH + firstBarLength });
@@ -16375,13 +16453,18 @@ const mainInit = () => {
16375
16453
  }
16376
16454
  });
16377
16455
 
16456
+ // ユーザカスタムイベント(フレーム毎、タイマー直前)
16457
+ safeExecuteCustomHooks(`g_customJsObj.mainBeforeFrameTimer`, g_customJsObj.mainBeforeFrameTimer, {
16458
+ arrowCnts, frzCnts, dummyArrowCnts, dummyFrzCnts,
16459
+ });
16460
+
16378
16461
  // 曲終了判定
16379
16462
  if (currentFrame >= fullFrame) {
16380
16463
  if (g_stateObj.lifeMode === C_LFE_BORDER && g_workObj.lifeVal < g_workObj.lifeBorder) {
16381
16464
  g_gameOverFlg = true;
16382
16465
  }
16383
16466
  resetKeyControl();
16384
- clearTimeout(g_timeoutEvtId);
16467
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
16385
16468
  g_workObj.mainEndTime = thisTime;
16386
16469
  resultInit();
16387
16470
 
@@ -16389,7 +16472,7 @@ const mainInit = () => {
16389
16472
 
16390
16473
  // ライフ制&ライフ0の場合は途中終了
16391
16474
  g_audio.pause();
16392
- clearTimeout(g_timeoutEvtId);
16475
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
16393
16476
  g_gameOverFlg = true;
16394
16477
  g_finishFlg = false;
16395
16478
  resultInit();
@@ -16428,7 +16511,7 @@ const mainInit = () => {
16428
16511
  g_scoreObj.frameNum++;
16429
16512
  g_scoreObj.baseFrame++;
16430
16513
  }
16431
- g_timeoutEvtId = setTimeout(flowTimeline, holdFrame ? g_maxFrameWait :
16514
+ g_timeoutEvtId = g_timerHandler.setTimeout(flowTimeline, holdFrame ? g_maxFrameWait :
16432
16515
  Math.min(Math.max(1000 / g_fps - buffTime, 0), g_maxFrameWait));
16433
16516
  }
16434
16517
  };
@@ -16442,7 +16525,7 @@ const mainInit = () => {
16442
16525
  */
16443
16526
  const cancelResumeCountdown = () => {
16444
16527
  if (countdownTimeoutId !== null) {
16445
- clearTimeout(countdownTimeoutId);
16528
+ g_timerHandler.clearTimeout(countdownTimeoutId);
16446
16529
  countdownTimeoutId = null;
16447
16530
  }
16448
16531
  document.getElementById(`lblResumeCountdown`)?.remove();
@@ -16491,7 +16574,7 @@ const mainInit = () => {
16491
16574
  // 一時停止時点で早期終了させる(位置復元はせず、通常終了時と同じ後片付けを行う)
16492
16575
  resetFrzReturn();
16493
16576
  }
16494
- clearTimeout(g_timeoutEvtId);
16577
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
16495
16578
  g_audio.pause();
16496
16579
 
16497
16580
  // フォーカスを失うとkeyupが届かなくなり、押しっぱなし判定・表示が残り得るため、
@@ -16564,7 +16647,7 @@ const mainInit = () => {
16564
16647
  manualPauseFlg = false;
16565
16648
  pausedElapsedTime = null;
16566
16649
  pausedStartAdjustment = null;
16567
- g_timeoutEvtId = setTimeout(flowTimeline, 1000 / g_fps);
16650
+ g_timeoutEvtId = g_timerHandler.setTimeout(flowTimeline, 1000 / g_fps);
16568
16651
  };
16569
16652
 
16570
16653
  const tick = _remaining => {
@@ -16573,7 +16656,7 @@ const mainInit = () => {
16573
16656
  return;
16574
16657
  }
16575
16658
  countdownLabel.innerHTML = String(_remaining);
16576
- countdownTimeoutId = setTimeout(() => tick(_remaining - 1), 1000);
16659
+ countdownTimeoutId = g_timerHandler.setTimeout(() => tick(_remaining - 1), 1000);
16577
16660
  };
16578
16661
  tick(3);
16579
16662
  };
@@ -16600,7 +16683,7 @@ const mainInit = () => {
16600
16683
  if (document.hidden) {
16601
16684
  pauseTimeline();
16602
16685
  } else {
16603
- g_timeoutEvtId = setTimeout(flowTimeline, 1000 / g_fps);
16686
+ g_timeoutEvtId = g_timerHandler.setTimeout(flowTimeline, 1000 / g_fps);
16604
16687
  }
16605
16688
  };
16606
16689
 
@@ -16864,7 +16947,7 @@ const appearKeyTypes = (_j, _targets, _alphas = fillArray(_targets.length, 1)) =
16864
16947
  const startFrzReturn = () => {
16865
16948
  if (!g_workObj.frzReturnFlg) {
16866
16949
  if (g_workObj.frzReturnTimerId) {
16867
- clearTimeout(g_workObj.frzReturnTimerId);
16950
+ g_timerHandler.clearTimeout(g_workObj.frzReturnTimerId);
16868
16951
  g_workObj.frzReturnTimerId = null;
16869
16952
  }
16870
16953
  lifeBarFrz.classList.remove(g_cssObj.life_frzNormal, g_cssObj.life_frzActive);
@@ -16882,7 +16965,7 @@ const startFrzReturn = () => {
16882
16965
  */
16883
16966
  const resetFrzReturn = () => {
16884
16967
  if (g_workObj.frzReturnTimerId) {
16885
- clearTimeout(g_workObj.frzReturnTimerId);
16968
+ g_timerHandler.clearTimeout(g_workObj.frzReturnTimerId);
16886
16969
  }
16887
16970
  g_workObj.frzReturnTimerId = null;
16888
16971
  g_workObj.frzReturnFlg = false;
@@ -16954,7 +17037,7 @@ const executeFrzReturn = (_seq, _idx, _axis) => {
16954
17037
 
16955
17038
  addTransform(`mainSprite`, `frzReturn`, _transform, g_transPriority.frzReturn);
16956
17039
 
16957
- g_workObj.frzReturnTimerId = setTimeout(() => executeFrzReturn(_seq, _idx + 1, _axis), 20);
17040
+ g_workObj.frzReturnTimerId = g_timerHandler.setTimeout(() => executeFrzReturn(_seq, _idx + 1, _axis), 20);
16958
17041
  };
16959
17042
 
16960
17043
  /**
@@ -16970,7 +17053,7 @@ const executeRetry = async (_logLabel = `Retry`) => {
16970
17053
  g_retryInProgress = true;
16971
17054
  try {
16972
17055
  g_audio.pause();
16973
- clearTimeout(g_timeoutEvtId);
17056
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
16974
17057
  clearWindow(`Main`);
16975
17058
  await musicAfterLoaded();
16976
17059
  await loadChartFile();
@@ -16993,7 +17076,7 @@ const quickRetry = (_retryCondition) => {
16993
17076
  return;
16994
17077
  }
16995
17078
  if (g_settings.autoRetryNum >= retryNum && !g_retryInProgress) {
16996
- setTimeout(async () => {
17079
+ g_timerHandler.setTimeout(async () => {
16997
17080
  await executeRetry(`AutoRetry`);
16998
17081
  }, 16);
16999
17082
  }
@@ -17240,6 +17323,7 @@ const judgeArrow = _j => {
17240
17323
  // 空押し判定(有効かつ早押し時のみ)
17241
17324
  displayDiff(_difFrame);
17242
17325
  stepHitTargetArrow(`Excessive`);
17326
+ safeExecuteCustomHooks(`g_customJsObj.judg_excessive`, g_customJsObj.judg_excessive, _difFrame, _j);
17243
17327
  return true;
17244
17328
 
17245
17329
  } else if (_difCnt <= g_judgObj.arrowJ[g_judgPosObj.shobon]) {
@@ -18288,8 +18372,8 @@ const resultInit = () => {
18288
18372
  if (g_finishFlg) {
18289
18373
  g_audio.pause();
18290
18374
  }
18291
- clearTimeout(g_timeoutEvtId);
18292
- clearTimeout(g_timeoutEvtResultId);
18375
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
18376
+ g_timerHandler.clearTimeout(g_timeoutEvtResultId);
18293
18377
  }, { ..._posObj, resetFunc: () => _func() }, _cssClass);
18294
18378
 
18295
18379
  /**
@@ -18358,14 +18442,14 @@ const resultInit = () => {
18358
18442
  // リザルト画面移行後のフェードアウト処理
18359
18443
  if (g_scoreObj.fadeOutFrame >= g_scoreObj.frameNum) {
18360
18444
  if (g_scoreObj.frameNum >= g_scoreObj.fullFrame) {
18361
- clearTimeout(g_timeoutEvtId);
18445
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
18362
18446
  }
18363
18447
  g_scoreObj.frameNum++;
18364
18448
  } else {
18365
18449
  const tmpVolume = (g_audio.volume - (3 * g_stateObj.volume / 100 * C_FRM_AFTERFADE / g_scoreObj.fadeOutTerm) / 1000);
18366
18450
  if (tmpVolume < 0) {
18367
18451
  g_audio.volume = 0;
18368
- clearTimeout(g_timeoutEvtId);
18452
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
18369
18453
  } else {
18370
18454
  g_audio.volume = tmpVolume;
18371
18455
  }
@@ -18376,7 +18460,7 @@ const resultInit = () => {
18376
18460
 
18377
18461
  g_scoreObj.resultFrameNum++;
18378
18462
  g_animationData.forEach(sprite => g_scoreObj[`${sprite}ResultFrameNum`]++);
18379
- g_timeoutEvtResultId = setTimeout(flowResultTimeline, 1000 / g_fps - buffTime);
18463
+ g_timeoutEvtResultId = g_timerHandler.setTimeout(flowResultTimeline, 1000 / g_fps - buffTime);
18380
18464
  };
18381
18465
  flowResultTimeline();
18382
18466
 
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2019/11/19
8
- * Revised : 2026/08/31 (v50.1.2)
8
+ * Revised : 2026/09/08 (v50.3.0)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -5244,6 +5244,8 @@ const g_customJsObj = {
5244
5244
  progress: [],
5245
5245
  main: [],
5246
5246
 
5247
+ preMakeArrow: [],
5248
+ preMakeFrzArrow: [],
5247
5249
  makeArrow: [],
5248
5250
  makeFrzArrow: [],
5249
5251
 
@@ -5259,11 +5261,13 @@ const g_customJsObj = {
5259
5261
  judg_uwan: [],
5260
5262
  judg_kita: [],
5261
5263
  judg_iknai: [],
5264
+ judg_excessive: [],
5262
5265
 
5263
5266
  judg_frzHit: [],
5264
5267
  judg_dummyFrzHit: [],
5265
5268
 
5266
5269
  mainEnterFrame: [],
5270
+ mainBeforeFrameTimer: [],
5267
5271
  result: [],
5268
5272
  resultEnterFrame: [],
5269
5273
  };
@@ -40,6 +40,16 @@ g_customJsObj.titleEnterFrame.push(() => {
40
40
 
41
41
  });
42
42
 
43
+ /**
44
+ * 選曲変更時(選曲モード時のみ)
45
+ * - この設定は譜面ヘッダーの|packageName=パッケージ名|を指定した場合のみ利用可能です。
46
+ * - 曲を変更したときに呼び出されます。
47
+ * @param {number} _musicIdxNum 選択曲番号
48
+ */
49
+ g_customJsObj.musicSelect.push((_musicIdxNum) => {
50
+
51
+ });
52
+
43
53
  /**
44
54
  * データ管理画面 [Scene: Data Management / Pear]
45
55
  */
@@ -55,7 +65,7 @@ g_customJsObj.precondition.push(() => {
55
65
  });
56
66
 
57
67
  /**
58
- * オプション画面(初期表示) [Scene: Option / Lime]
68
+ * Settings画面(初期表示) [Scene: Option / Lime]
59
69
  */
60
70
  g_customJsObj.option.push(() => {
61
71
 
@@ -71,19 +81,36 @@ g_customJsObj.difficulty.push((_initFlg, _canLoadDifInfoFlg) => {
71
81
  });
72
82
 
73
83
  /**
74
- * 表示変更(初期表示) [Scene: Settings-Display / Lemon]
84
+ * Display画面(初期表示) [Scene: Settings-Display / Lemon]
75
85
  */
76
86
  g_customJsObj.settingsDisplay.push(() => {
77
87
 
78
88
  });
79
89
 
80
90
  /**
81
- * 表示変更(初期表示) [Scene: Ex-Settings / apple]
91
+ * Display画面(プレビュー表示) [Scene: Settings-Display / Lemon]
92
+ * @param {object} _parent Display設定のプレビューの親要素
93
+ * @param {number} _playingWidth プレイ画面の横幅(px)
94
+ * @param {number} _playingHeight プレイ画面の高さ(px)
95
+ */
96
+ g_customJsObj.displayPreview.push((_parent, _playingWidth, _playingHeight) => {
97
+
98
+ });
99
+
100
+ /**
101
+ * Ex-Settings画面(初期表示) [Scene: Ex-Settings / apple]
82
102
  */
83
103
  g_customJsObj.exSetting.push(() => {
84
104
 
85
105
  });
86
106
 
107
+ /**
108
+ * 設定サマリー表示描画時
109
+ */
110
+ g_customJsObj.settingSummary.push(() => {
111
+
112
+ });
113
+
87
114
  /**
88
115
  * キーコンフィグ画面(初期表示) [Scene: KeyConfig / Orange]
89
116
  */
@@ -125,98 +152,152 @@ g_customJsObj.main.push(() => {
125
152
  });
126
153
 
127
154
  /**
128
- * 矢印生成
155
+ * 矢印生成前(g_attrObj定義後)
156
+ * - g_attrObjの書き換えを行いたいときなどの使用を想定
129
157
  * @param {object} _attrs 矢印属性
130
158
  * @param {string} _arrowName 矢印名
131
159
  * @param {string} _name 矢印識別名
132
160
  * @param {number} _arrowCnt 矢印番号
133
161
  */
134
- g_customJsObj.makeArrow.push((_attrs, _arrowName, _name, _arrowCnt) => {
162
+ g_customJsObj.preMakeArrow.push((_attrs, _arrowName, _name, _arrowCnt) => {
135
163
 
136
164
  });
137
165
 
138
166
  /**
139
- * フリーズアロー生成
167
+ * 矢印生成
140
168
  * @param {object} _attrs 矢印属性
141
169
  * @param {string} _arrowName 矢印名
142
170
  * @param {string} _name 矢印識別名
143
171
  * @param {number} _arrowCnt 矢印番号
144
172
  */
173
+ g_customJsObj.makeArrow.push((_attrs, _arrowName, _name, _arrowCnt) => {
174
+
175
+ });
176
+
177
+ /**
178
+ * フリーズアロー生成前(g_attrObj定義後)
179
+ * - g_attrObjの書き換えを行いたいときなどの使用を想定
180
+ * @param {object} _attrs フリーズアロー属性
181
+ * @param {string} _arrowName フリーズアロー名
182
+ * @param {string} _name フリーズアロー識別名
183
+ * @param {number} _arrowCnt フリーズアロー番号
184
+ */
185
+ g_customJsObj.preMakeFrzArrow.push((_attrs, _arrowName, _name, _arrowCnt) => {
186
+
187
+ });
188
+
189
+ /**
190
+ * フリーズアロー生成
191
+ * @param {object} _attrs フリーズアロー属性
192
+ * @param {string} _arrowName フリーズアロー名
193
+ * @param {string} _name フリーズアロー識別名
194
+ * @param {number} _arrowCnt フリーズアロー番号
195
+ */
145
196
  g_customJsObj.makeFrzArrow.push((_attrs, _arrowName, _name, _arrowCnt) => {
146
197
 
147
198
  });
148
199
 
149
200
  /**
150
201
  * ダミー矢印判定時
202
+ * @param {number} _j レーン番号
151
203
  */
152
- g_customJsObj.dummyArrow.push(() => {
204
+ g_customJsObj.dummyArrow.push((_j) => {
153
205
 
154
206
  });
155
207
 
156
208
  /**
157
209
  * ダミーフリーズアロー判定時
210
+ * @param {number} _j レーン番号
211
+ */
212
+ g_customJsObj.dummyFrz.push((_j) => {
213
+
214
+ });
215
+
216
+ /**
217
+ * Appearanceフィルター動作時
218
+ * @param {number} _topNum 上部のフィルターに対応するmainSpriteの番号
219
+ * @param {number} _bottomNum 下部のフィルターに対応するmainSpriteの番号
158
220
  */
159
- g_customJsObj.dummyFrz.push(() => {
221
+ g_customJsObj.appearanceFilter.push((_topNum, _bottomNum) => {
160
222
 
161
223
  });
162
224
 
163
225
  /**
164
226
  * 判定カスタム処理 (引数は共通で1つ保持)
165
227
  * @param {number} _difFrame タイミング誤差(フレーム数)
228
+ * @param {number} _j レーン番号
166
229
  */
167
230
  // イイ
168
- g_customJsObj.judg_ii.push((_difFrame) => {
231
+ g_customJsObj.judg_ii.push((_difFrame, _j) => {
169
232
 
170
233
  });
171
234
 
172
235
  // シャキン
173
- g_customJsObj.judg_shakin.push((_difFrame) => {
236
+ g_customJsObj.judg_shakin.push((_difFrame, _j) => {
174
237
 
175
238
  });
176
239
 
177
240
  // マターリ
178
- g_customJsObj.judg_matari.push((_difFrame) => {
241
+ g_customJsObj.judg_matari.push((_difFrame, _j) => {
179
242
 
180
243
  });
181
244
 
182
245
  // ショボーン
183
- g_customJsObj.judg_shobon.push((_difFrame) => {
246
+ g_customJsObj.judg_shobon.push((_difFrame, _j) => {
184
247
 
185
248
  });
186
249
 
187
250
  // ウワァン
188
- g_customJsObj.judg_uwan.push((_difFrame) => {
251
+ g_customJsObj.judg_uwan.push((_difFrame, _j) => {
189
252
 
190
253
  });
191
254
 
192
255
  // キター
193
- g_customJsObj.judg_kita.push((_difFrame) => {
256
+ g_customJsObj.judg_kita.push((_difFrame, _j) => {
194
257
 
195
258
  });
196
259
 
197
260
  // イクナイ
198
- g_customJsObj.judg_iknai.push((_difFrame) => {
261
+ g_customJsObj.judg_iknai.push((_difFrame, _j) => {
262
+
263
+ });
264
+
265
+ // Excessive
266
+ g_customJsObj.judg_excessive.push((_difFrame, _j) => {
199
267
 
200
268
  });
201
269
 
202
270
  // 通常フリーズアローヒット時
203
- g_customJsObj.judg_frzHit.push((_difFrame) => {
271
+ g_customJsObj.judg_frzHit.push((_difFrame, _j) => {
204
272
 
205
273
  });
206
274
 
207
275
  // ダミーフリーズアローヒット時
208
- g_customJsObj.judg_dummyFrzHit.push((_difFrame) => {
276
+ g_customJsObj.judg_dummyFrzHit.push((_difFrame, _j) => {
209
277
 
210
278
  });
211
279
 
212
280
  /**
213
281
  * メイン画面(フレーム毎表示) [Scene: Main / Banana]
214
282
  * - 現在のフレーム数は g_scoreObj.baseFrame で取得可能
283
+ * - 矢印生成、移動前に処理を行いたいときに記述
215
284
  */
216
285
  g_customJsObj.mainEnterFrame.push(() => {
217
286
 
218
287
  });
219
288
 
289
+ /**
290
+ * メイン画面(フレーム毎表示、タイマー直前)
291
+ * - 矢印生成、移動後に処理を行いたいときに記述
292
+ * @param {number[]} arrowCnts 矢印カウント情報
293
+ * @param {number[]} frzCnts フリーズアローカウント情報
294
+ * @param {number[]} dummyArrowCnts ダミー矢印カウント情報
295
+ * @param {number[]} dummyFrzCnts ダミーフリーズアローカウント情報
296
+ */
297
+ g_customJsObj.mainBeforeFrameTimer.push(({ arrowCnts, frzCnts, dummyArrowCnts, dummyFrzCnts }) => {
298
+
299
+ });
300
+
220
301
  /**
221
302
  * 結果画面(初期表示) [Scene: Result / Grape]
222
303
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "danoniplus",
3
- "version": "50.1.2",
3
+ "version": "50.3.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",