danoniplus 41.1.0 → 41.2.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 : 2025/05/05
7
+ * Revised : 2025/05/09
8
8
  *
9
9
  * https://github.com/cwtickle/danoniplus
10
10
  */
11
- const g_version = `Ver 41.1.0`;
12
- const g_revisedDate = `2025/05/05`;
11
+ const g_version = `Ver 41.2.0`;
12
+ const g_revisedDate = `2025/05/09`;
13
13
 
14
14
  // カスタム用バージョン (danoni_custom.js 等で指定可)
15
15
  let g_localVersion = ``;
@@ -2310,6 +2310,25 @@ class AudioPlayer {
2310
2310
  }
2311
2311
  }
2312
2312
 
2313
+ close() {
2314
+ if (this._context) {
2315
+ this._context.close()?.catch(() => {/* ignore double-close */ });
2316
+ this._buffer = null;
2317
+ }
2318
+ if (this._source) {
2319
+ this._source.disconnect(this._gain);
2320
+ this._source = null;
2321
+ }
2322
+ if (this._gain) {
2323
+ this._gain.disconnect();
2324
+ this._gain = null;
2325
+ }
2326
+ }
2327
+
2328
+ get elapsedTime() {
2329
+ return this._context.currentTime - this._startTime + this._fadeinPosition;
2330
+ }
2331
+
2313
2332
  set currentTime(_currentTime) {
2314
2333
  this._fadeinPosition = _currentTime;
2315
2334
  }
@@ -2957,6 +2976,21 @@ const copySetColor = (_baseObj, _scoreId) => {
2957
2976
  const getMusicUrl = _scoreId =>
2958
2977
  g_headerObj.musicUrls?.[g_headerObj.musicNos[_scoreId]] ?? g_headerObj.musicUrls?.[0] ?? `nosound.mp3`;
2959
2978
 
2979
+ /**
2980
+ * 音源データの実際のパスを取得
2981
+ * @param {string} _musicUrl
2982
+ * @returns {string}
2983
+ */
2984
+ const getFullMusicUrl = (_musicUrl = ``) => {
2985
+ let url = `${g_rootPath}../${g_headerObj.musicFolder}/${_musicUrl}`;
2986
+ if (_musicUrl.indexOf(C_MRK_CURRENT_DIRECTORY) !== -1) {
2987
+ url = _musicUrl.split(C_MRK_CURRENT_DIRECTORY)[1];
2988
+ } else if (g_headerObj.musicFolder.indexOf(C_MRK_CURRENT_DIRECTORY) !== -1) {
2989
+ url = `${g_headerObj.musicFolder.split(C_MRK_CURRENT_DIRECTORY)[1]}/${_musicUrl}`;
2990
+ }
2991
+ return url;
2992
+ }
2993
+
2960
2994
  /**
2961
2995
  * 譜面ファイル読込後処理(譜面詳細情報取得用)
2962
2996
  * @param {number} _scoreId
@@ -3755,7 +3789,22 @@ const headerConvert = _dosObj => {
3755
3789
 
3756
3790
  // 楽曲URL
3757
3791
  if (hasVal(_dosObj.musicUrl)) {
3758
- obj.musicUrls = splitLF2(_dosObj.musicUrl);
3792
+ const musicUrls = splitLF2(_dosObj.musicUrl);
3793
+ obj.musicUrls = [], obj.musicStarts = [], obj.musicEnds = [];
3794
+ musicUrls.forEach((val, j) => {
3795
+ const musicUrlPair = val.split(`,`);
3796
+ obj.musicUrls[j] = musicUrlPair[0] || ``;
3797
+ if (musicUrlPair[1] !== undefined) {
3798
+ const musicBGMTime = musicUrlPair[1].split(`-`).map(str => str.trim());
3799
+ obj.musicStarts[j] = Math.floor(transTimerToFrame(musicBGMTime[0] ?? 0) / g_fps);
3800
+ obj.musicEnds[j] = musicBGMTime[1] !== undefined ?
3801
+ Math.floor((transTimerToFrame(musicBGMTime[1] ?? 0)) / g_fps) :
3802
+ Math.floor((transTimerToFrame(musicBGMTime[0] ?? 0) + transTimerToFrame(`0:20`)) / g_fps);
3803
+ } else {
3804
+ obj.musicStarts[j] = 0;
3805
+ obj.musicEnds[j] = 20;
3806
+ }
3807
+ });
3759
3808
  } else {
3760
3809
  makeWarningWindow(g_msgInfoObj.E_0031);
3761
3810
  }
@@ -4907,6 +4956,7 @@ const titleInit = (_initFlg = false) => {
4907
4956
 
4908
4957
  // 選曲画面の初期化
4909
4958
  const wheelCycle = 2;
4959
+ g_settings.musicLoopNum = 0;
4910
4960
 
4911
4961
  /**
4912
4962
  * メイン以外の選曲ボタンの作成
@@ -4921,6 +4971,17 @@ const titleInit = (_initFlg = false) => {
4921
4971
  align: C_ALIGN_LEFT, padding: `0 10px`,
4922
4972
  }, g_cssObj.button_Default_NoColor, g_cssObj.title_base);
4923
4973
 
4974
+ /**
4975
+ * 選曲画面上の音量調整
4976
+ * @param {number} _num
4977
+ */
4978
+ const setBGMVolume = (_num = 1) => {
4979
+ g_settings.bgmVolumeNum = nextPos(g_settings.bgmVolumeNum, _num, g_settings.volumes.length);
4980
+ g_stateObj.bgmVolume = g_settings.volumes[g_settings.bgmVolumeNum];
4981
+ g_audio.volume = g_stateObj.bgmVolume / 100;
4982
+ btnBgmVolume.textContent = `${g_stateObj.bgmVolume}${g_lblNameObj.percent}`;
4983
+ };
4984
+
4924
4985
  for (let j = -g_settings.mSelectableTerms; j <= g_settings.mSelectableTerms; j++) {
4925
4986
  if (j !== 0) {
4926
4987
  divRoot.appendChild(createMSelectBtn(j));
@@ -4933,6 +4994,7 @@ const titleInit = (_initFlg = false) => {
4933
4994
  createCss2Button(`btnStart`,
4934
4995
  `>`, () => {
4935
4996
  clearTimeout(g_timeoutEvtTitleId);
4997
+ pauseBGM();
4936
4998
  g_handler.removeListener(wheelHandler);
4937
4999
  g_keyObj.prevKey = `Dummy${g_settings.musicIdxNum}`;
4938
5000
  }, Object.assign({
@@ -4943,10 +5005,25 @@ const titleInit = (_initFlg = false) => {
4943
5005
  createCss2Button(`btnMusicSelectNext`, `↓`, () => changeMSelect(1),
4944
5006
  g_lblPosObj.btnMusicSelectNext, g_cssObj.button_Setting),
4945
5007
  createCss2Button(`btnMusicSelectRandom`, `Random`, () =>
4946
- changeMSelect(g_headerObj.musicIdxList[Math.floor(Math.random() * g_headerObj.musicIdxList.length)]),
5008
+ changeMSelect(Math.floor(Math.random() * (g_headerObj.musicIdxList.length - 1)) + 1),
4947
5009
  g_lblPosObj.btnMusicSelectRandom, g_cssObj.button_Default),
4948
5010
  createDivCss2Label(`lblMusicCnt`, ``, g_lblPosObj.lblMusicCnt),
4949
5011
  createDivCss2Label(`lblComment`, ``, g_lblPosObj.lblComment_music),
5012
+
5013
+ createDivCss2Label(`lblBgmVolume`, `BGM Volume`, g_lblPosObj.lblBgmVolume),
5014
+ createCss2Button(`btnBgmMute`, g_stateObj.bgmMuteFlg ? `&#x1f507;` : `&#x1f50a;`, evt => {
5015
+ g_stateObj.bgmMuteFlg = !g_stateObj.bgmMuteFlg;
5016
+ g_stateObj.bgmMuteFlg ? pauseBGM() : playBGM(0);
5017
+ evt.target.innerHTML = g_stateObj.bgmMuteFlg ? `&#x1f507;` : `&#x1f50a;`;
5018
+ }, g_lblPosObj.btnBgmMute, g_cssObj.button_Default),
5019
+ createCss2Button(`btnBgmVolume`, `${g_stateObj.bgmVolume}${g_lblNameObj.percent}`, () => setBGMVolume(),
5020
+ Object.assign({
5021
+ cxtFunc: () => setBGMVolume(-1),
5022
+ }, g_lblPosObj.btnBgmVolume), g_cssObj.button_Default),
5023
+ createCss2Button(`btnBgmVolumeL`, `<`, () => setBGMVolume(-1),
5024
+ g_lblPosObj.btnBgmVolumeL, g_cssObj.button_Setting),
5025
+ createCss2Button(`btnBgmVolumeR`, `>`, () => setBGMVolume(),
5026
+ g_lblPosObj.btnBgmVolumeR, g_cssObj.button_Setting),
4950
5027
  );
4951
5028
  changeMSelect(0, _initFlg);
4952
5029
 
@@ -4976,6 +5053,7 @@ const titleInit = (_initFlg = false) => {
4976
5053
 
4977
5054
  // 初期表示用 (2秒後に選曲画面を表示)
4978
5055
  if (_initFlg && !g_headerObj.customTitleUse) {
5056
+ g_audio.muted = true;
4979
5057
  const mSelectTitleSprite = createEmptySprite(divRoot, `mSelectTitleSprite`,
4980
5058
  g_windowObj.mSelectTitleSprite, g_cssObj.settings_DifSelector);
4981
5059
  multiAppend(mSelectTitleSprite,
@@ -4994,6 +5072,8 @@ const titleInit = (_initFlg = false) => {
4994
5072
  if (_opacity <= 0) {
4995
5073
  clearTimeout(fadeOpacity);
4996
5074
  mSelectTitleSprite.style.display = C_DIS_NONE;
5075
+ g_audio.muted = false;
5076
+ g_audio.currentTime = g_headerObj.musicStarts[g_headerObj.musicIdxList[g_settings.musicIdxNum]] ?? 0;
4997
5077
  } else {
4998
5078
  mSelectTitleSprite.style.opacity = _opacity;
4999
5079
  fadeOpacity = setTimeout(() => {
@@ -5278,6 +5358,173 @@ const getCreatorInfo = (_creatorList) => {
5278
5358
  return [creatorName, creatorUrl, creatorIdx];
5279
5359
  }
5280
5360
 
5361
+ /**
5362
+ * BGMの停止
5363
+ */
5364
+ const pauseBGM = () => {
5365
+ if (g_audio) {
5366
+ g_handler.removeListener(g_stateObj.bgmTimeupdateEvtId);
5367
+ g_audio.pause();
5368
+ if (!(g_audio instanceof AudioPlayer)) {
5369
+ g_audio.removeAttribute('src');
5370
+ g_audio.load();
5371
+ }
5372
+ }
5373
+ [`bgmLooped`, `bgmFadeIn`, `bgmFadeOut`].forEach(id => {
5374
+ if (g_stateObj[id]) {
5375
+ clearInterval(g_stateObj[id]);
5376
+ g_stateObj[id] = null;
5377
+ }
5378
+ });
5379
+ };
5380
+
5381
+ /**
5382
+ * BGM再生処理
5383
+ * @param {number} _num
5384
+ * @param {number} _currentLoopNum
5385
+ */
5386
+ const playBGM = async (_num, _currentLoopNum = g_settings.musicLoopNum) => {
5387
+ const FADE_STEP = 0.05 * g_stateObj.bgmVolume / 100;
5388
+ const FADE_INTERVAL_MS = 100;
5389
+ const FADE_DELAY_MS = 500;
5390
+
5391
+ const musicUrl = getMusicUrl(g_headerObj.viewLists[0]);
5392
+ const url = getFullMusicUrl(musicUrl);
5393
+ const encodeFlg = listMatching(musicUrl, [`.js`, `.txt`], { suffix: `$` });
5394
+ const musicStart = g_headerObj.musicStarts?.[g_headerObj.musicIdxList[g_settings.musicIdxNum]] ?? 0;
5395
+ const musicEnd = g_headerObj.musicEnds?.[g_headerObj.musicIdxList[g_settings.musicIdxNum]] ?? 0;
5396
+
5397
+ /**
5398
+ * BGMのフェードアウトとシーク
5399
+ */
5400
+ const fadeOutAndSeek = () => {
5401
+ let volume = g_audio.volume;
5402
+ const fadeInterval = setInterval(() => {
5403
+ if (volume > FADE_STEP && g_currentPage === `title`) {
5404
+ volume -= FADE_STEP;
5405
+ g_audio.volume = Math.max(volume, 0);
5406
+ } else {
5407
+ clearInterval(fadeInterval);
5408
+ g_stateObj.bgmFadeOut = null;
5409
+ g_audio.pause();
5410
+ g_audio.currentTime = musicStart;
5411
+
5412
+ // フェードイン開始
5413
+ if (g_currentPage === `title`) {
5414
+ setTimeout(() => {
5415
+ fadeIn();
5416
+ if (encodeFlg) {
5417
+ // base64エンコード時はtimeupdateイベントが発火しないため、
5418
+ // setIntervalで時間を取得する
5419
+ repeatBGM();
5420
+ }
5421
+ }, FADE_DELAY_MS);
5422
+ } else {
5423
+ pauseBGM();
5424
+ }
5425
+ }
5426
+ }, FADE_INTERVAL_MS);
5427
+ g_stateObj.bgmFadeOut = fadeInterval;
5428
+ };
5429
+
5430
+ /**
5431
+ * BGMのフェードイン
5432
+ */
5433
+ const fadeIn = () => {
5434
+ if (!(g_audio instanceof AudioPlayer) && !g_audio.src) {
5435
+ return;
5436
+ }
5437
+ let volume = 0;
5438
+ g_audio.play();
5439
+ const fadeInterval = setInterval(() => {
5440
+ if (volume < g_stateObj.bgmVolume / 100 && g_currentPage === `title`) {
5441
+ volume += FADE_STEP;
5442
+ g_audio.volume = Math.min(volume, 1);
5443
+ } else {
5444
+ clearInterval(fadeInterval);
5445
+ g_stateObj.bgmFadeIn = null;
5446
+ }
5447
+ }, FADE_INTERVAL_MS);
5448
+ g_stateObj.bgmFadeIn = fadeInterval;
5449
+ };
5450
+
5451
+ /**
5452
+ * BGMのループ処理
5453
+ */
5454
+ const repeatBGM = () => {
5455
+ if (encodeFlg) {
5456
+ // base64エンコード時はtimeupdateイベントが発火しないため、setIntervalで時間を取得する
5457
+ const repeatCheck = setInterval((num = g_settings.musicIdxNum) => {
5458
+ try {
5459
+ if (((g_audio.elapsedTime >= musicEnd) ||
5460
+ num !== g_settings.musicIdxNum) && g_stateObj.bgmLooped !== null) {
5461
+ clearInterval(repeatCheck);
5462
+ g_stateObj.bgmLooped = null;
5463
+ fadeOutAndSeek();
5464
+ }
5465
+ } catch (e) {
5466
+ clearInterval(repeatCheck);
5467
+ g_stateObj.bgmLooped = null;
5468
+ }
5469
+ }, FADE_INTERVAL_MS);
5470
+ g_stateObj.bgmLooped = repeatCheck;
5471
+
5472
+ } else {
5473
+ g_stateObj.bgmTimeupdateEvtId = g_handler.addListener(g_audio, "timeupdate", () => {
5474
+ if (g_audio.currentTime >= musicEnd) {
5475
+ fadeOutAndSeek();
5476
+ }
5477
+ });
5478
+ }
5479
+ };
5480
+
5481
+ if (encodeFlg) {
5482
+ try {
5483
+ // base64エンコードは読込に時間が掛かるため、曲変更時のみ読込
5484
+ if (!hasVal(g_musicdata) || Math.abs(_num) % g_headerObj.musicIdxList.length !== 0) {
5485
+ await loadScript2(url);
5486
+ musicInit();
5487
+ if (_currentLoopNum !== g_settings.musicLoopNum) {
5488
+ return;
5489
+ }
5490
+ const tmpAudio = new AudioPlayer();
5491
+ const array = Uint8Array.from(atob(g_musicdata), v => v.charCodeAt(0));
5492
+ await tmpAudio.init(array.buffer);
5493
+ if (_currentLoopNum !== g_settings.musicLoopNum) {
5494
+ tmpAudio.close();
5495
+ return;
5496
+ }
5497
+ g_audio = tmpAudio;
5498
+ }
5499
+ g_audio.volume = g_stateObj.bgmVolume / 100;
5500
+ if (g_currentPage === `title`) {
5501
+ g_audio.currentTime = musicStart;
5502
+ g_audio.play();
5503
+ }
5504
+ } catch (e) {
5505
+ // 音源の読み込みに失敗した場合、エラーを表示
5506
+ console.warn(`BGM load error: ${e}`);
5507
+ }
5508
+
5509
+ } else {
5510
+ g_audio = new Audio();
5511
+ g_audio.src = url;
5512
+ g_audio.autoplay = false;
5513
+ g_audio.volume = g_stateObj.bgmVolume / 100;
5514
+ const loadedMeta = g_handler.addListener(g_audio, `loadedmetadata`, () => {
5515
+ g_handler.removeListener(loadedMeta);
5516
+ if (_currentLoopNum !== g_settings.musicLoopNum) {
5517
+ return;
5518
+ }
5519
+ g_audio.currentTime = musicStart;
5520
+ g_audio.play();
5521
+ }, { once: true });
5522
+ }
5523
+ if (musicEnd > 0) {
5524
+ repeatBGM();
5525
+ }
5526
+ };
5527
+
5281
5528
  /**
5282
5529
  * 選曲ボタンを押したときの処理
5283
5530
  * @param {number} _num
@@ -5285,6 +5532,7 @@ const getCreatorInfo = (_creatorList) => {
5285
5532
  */
5286
5533
  const changeMSelect = (_num, _initFlg = false) => {
5287
5534
  const limitedMLength = 35;
5535
+ pauseBGM();
5288
5536
 
5289
5537
  // 選択方向に合わせて楽曲リスト情報を再取得
5290
5538
  for (let j = -g_settings.mSelectableTerms; j <= g_settings.mSelectableTerms; j++) {
@@ -5301,6 +5549,10 @@ const changeMSelect = (_num, _initFlg = false) => {
5301
5549
  // 現在選択中の楽曲IDを再設定
5302
5550
  g_settings.musicIdxNum = (g_settings.musicIdxNum + _num + g_headerObj.musicIdxList.length) % g_headerObj.musicIdxList.length;
5303
5551
 
5552
+ // 楽曲の多重読込防止(この値が変化していれば読み込まない)
5553
+ g_settings.musicLoopNum++;
5554
+ const currentLoopNum = g_settings.musicLoopNum;
5555
+
5304
5556
  // 選択した楽曲に対応する譜面番号、製作者情報、曲長を取得
5305
5557
  g_headerObj.viewLists = [];
5306
5558
  const tmpKeyList = [], tmpCreatorList = [], tmpPlayingFrameList = [], tmpBpmList = [];
@@ -5346,7 +5598,7 @@ const changeMSelect = (_num, _initFlg = false) => {
5346
5598
  viewKeyStorage.cache = new Map();
5347
5599
 
5348
5600
  // 初期化もしくは楽曲変更時に速度を初期化
5349
- if (_initFlg || _num !== 0) {
5601
+ if (_initFlg || Math.abs(_num) % g_headerObj.musicIdxList.length !== 0) {
5350
5602
  g_stateObj.speed = g_headerObj.initSpeeds[g_headerObj.viewLists[0]];
5351
5603
  g_settings.speedNum = getCurrentNo(g_settings.speeds, g_stateObj.speed);
5352
5604
  }
@@ -5354,6 +5606,19 @@ const changeMSelect = (_num, _initFlg = false) => {
5354
5606
  // コメント文の加工
5355
5607
  lblComment.innerHTML = convertStrToVal(g_headerObj[`commentVal${g_settings.musicIdxNum}`]);
5356
5608
 
5609
+ // BGM再生処理
5610
+ if (!g_stateObj.bgmMuteFlg) {
5611
+ if (_initFlg) {
5612
+ playBGM(_num);
5613
+ } else {
5614
+ setTimeout(() => {
5615
+ if (currentLoopNum === g_settings.musicLoopNum) {
5616
+ playBGM(_num, currentLoopNum);
5617
+ }
5618
+ }, 500);
5619
+ }
5620
+ }
5621
+
5357
5622
  // 選曲変更時のカスタム関数実行
5358
5623
  g_customJsObj.musicSelect.forEach(func => func(g_settings.musicIdxNum));
5359
5624
  };
@@ -8875,13 +9140,7 @@ const loadMusic = () => {
8875
9140
  g_currentPage = `loading`;
8876
9141
 
8877
9142
  const musicUrl = getMusicUrl(g_stateObj.scoreId);
8878
- let url = `${g_rootPath}../${g_headerObj.musicFolder}/${musicUrl}`;
8879
- if (musicUrl.indexOf(C_MRK_CURRENT_DIRECTORY) !== -1) {
8880
- url = musicUrl.split(C_MRK_CURRENT_DIRECTORY)[1];
8881
- } else if (g_headerObj.musicFolder.indexOf(C_MRK_CURRENT_DIRECTORY) !== -1) {
8882
- url = `${g_headerObj.musicFolder.split(C_MRK_CURRENT_DIRECTORY)[1]}/${musicUrl}`;
8883
- }
8884
-
9143
+ const url = getFullMusicUrl(musicUrl);
8885
9144
  g_headerObj.musicUrl = musicUrl;
8886
9145
  g_musicEncodedFlg = listMatching(musicUrl, [`.js`, `.txt`], { suffix: `$` });
8887
9146
 
@@ -8943,6 +9202,7 @@ const setAudio = async (_url) => {
8943
9202
 
8944
9203
  const loadMp3 = () => {
8945
9204
  if (g_isFile) {
9205
+ g_audio = new Audio();
8946
9206
  g_audio.src = _url;
8947
9207
  musicAfterLoaded();
8948
9208
  } else {
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2019/11/19
8
- * Revised : 2025/04/21 (v41.0.2)
8
+ * Revised : 2025/05/09 (v41.2.0)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -288,6 +288,21 @@ const updateWindowSiz = () => {
288
288
  siz: g_limitObj.difSelectorSiz, align: C_ALIGN_LEFT,
289
289
  overflow: C_DIS_AUTO, whiteSpace: `normal`,
290
290
  },
291
+ btnBgmMute: {
292
+ x: g_btnX() + 90, y: g_sHeight - 105, w: 40, h: 35, siz: 30,
293
+ },
294
+ lblBgmVolume: {
295
+ x: g_btnX(), y: g_sHeight - 85, w: g_btnWidth(1 / 4), h: 20, siz: 12, align: C_ALIGN_LEFT,
296
+ },
297
+ btnBgmVolume: {
298
+ x: g_btnX() + 20, y: g_sHeight - 70, w: g_btnWidth(1 / 4) - 40, h: 20, siz: 14,
299
+ },
300
+ btnBgmVolumeL: {
301
+ x: g_btnX(), y: g_sHeight - 70, w: 20, h: 20, siz: 12,
302
+ },
303
+ btnBgmVolumeR: {
304
+ x: g_btnX() + g_btnWidth(1 / 4) - 20, y: g_sHeight - 70, w: 20, h: 20, siz: 12,
305
+ },
291
306
 
292
307
  /** データ管理 */
293
308
  btnResetBack: {
@@ -999,6 +1014,13 @@ let C_WOD_FRAME = 30;
999
1014
  // 譜面データ持ち回り用
1000
1015
  const g_stateObj = {
1001
1016
  keyInitial: false,
1017
+ bgmVolume: 50,
1018
+ bgmLooped: null,
1019
+ bgmFadeIn: null,
1020
+ bgmFadeOut: null,
1021
+ bgmTimeupdateEvtId: null,
1022
+ bgmMuteFlg: false,
1023
+
1002
1024
  dosDivideFlg: false,
1003
1025
  scoreLockFlg: false,
1004
1026
  scoreId: 0,
@@ -1015,6 +1037,7 @@ const g_stateObj = {
1015
1037
  hitPosition: 0,
1016
1038
  fadein: 0,
1017
1039
  volume: 100,
1040
+
1018
1041
  lifeRcv: 2,
1019
1042
  lifeDmg: 7,
1020
1043
  lifeMode: `Border`,
@@ -1115,6 +1138,7 @@ const makeSpeedList = (_minSpd, _maxSpd) => [...Array((_maxSpd - _minSpd) * 20 +
1115
1138
  const g_settings = {
1116
1139
 
1117
1140
  musicIdxNum: 0,
1141
+ musicLoopNum: 0,
1118
1142
  dataMgtNum: {
1119
1143
  environment: 0,
1120
1144
  highscores: 0,
@@ -1163,6 +1187,7 @@ const g_settings = {
1163
1187
 
1164
1188
  volumes: [0, 0.5, 1, 2, 5, 10, 25, 50, 75, 100],
1165
1189
  volumeNum: 0,
1190
+ bgmVolumeNum: 0,
1166
1191
 
1167
1192
  appearances: [`Visible`, `Hidden`, `Hidden+`, `Sudden`, `Sudden+`, `Hid&Sud+`],
1168
1193
  appearanceNum: 0,
@@ -1239,6 +1264,7 @@ const g_settings = {
1239
1264
  };
1240
1265
 
1241
1266
  g_settings.volumeNum = g_settings.volumes.length - 1;
1267
+ g_settings.bgmVolumeNum = roundZero(g_settings.volumes.findIndex(v => v === g_stateObj.bgmVolume));
1242
1268
  g_settings.opacityNum = g_settings.opacitys.length - 1;
1243
1269
 
1244
1270
  /**
@@ -2075,6 +2101,10 @@ const g_shortcutObj = {
2075
2101
  KeyD: { id: `btnReset` },
2076
2102
  ArrowUp: { id: `btnMusicSelectPrev` },
2077
2103
  ArrowDown: { id: `btnMusicSelectNext` },
2104
+ ArrowLeft: { id: `btnBgmVolumeL` },
2105
+ ArrowRight: { id: `btnBgmVolumeR` },
2106
+ KeyM: { id: `btnBgmMute` },
2107
+ KeyR: { id: `btnMusicSelectRandom` },
2078
2108
  },
2079
2109
  dataMgt: {
2080
2110
  KeyE: { id: `btnEnvironment` },
@@ -2433,7 +2463,7 @@ Object.keys(g_btnWaitFrame).forEach(key => {
2433
2463
  // - btn + プロパティ名に合致するボタンid名に対して、
2434
2464
  // どの位置(X方向)にショートカット名を表示するかを設定
2435
2465
  const g_btnPatterns = {
2436
- title: { Start: 0, Comment: -10 },
2466
+ title: { Start: 0, Comment: -10, MusicSelectRandom: -10 },
2437
2467
  dataMgt: { Back: 0, Environment: -35, Highscores: -35, CustomKey: -35, Others: -35 },
2438
2468
  precondition: { Back: 0 },
2439
2469
  option: { Back: 0, KeyConfig: 0, Play: 0, Display: -5, Save: -10, Graph: -25 },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "danoniplus",
3
- "version": "41.1.0",
3
+ "version": "41.2.0",
4
4
  "description": "Dancing☆Onigiri (CW Edition) - Web-based Rhythm Game",
5
5
  "main": "./js/danoni_main.js",
6
6
  "scripts": {